Add mapping for json files

This commit is contained in:
Manfred Karrer 2017-04-19 19:41:40 -05:00
parent db73db02a6
commit a65a28855f
15 changed files with 532 additions and 202 deletions

View file

@ -0,0 +1,102 @@
/*
* 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 io.bisq.common.storage;
import io.bisq.common.UserThread;
import io.bisq.common.util.Utilities;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Paths;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Slf4j
public class JsonFileManager {
private final ThreadPoolExecutor executor = Utilities.getThreadPoolExecutor("saveToDiscExecutor", 5, 50, 60);
private File dir;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public JsonFileManager(File dir) {
this.dir = dir;
if (!dir.exists())
if (!dir.mkdir())
log.warn("make dir failed");
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
UserThread.execute(JsonFileManager.this::shutDown);
}, "WriteOnlyFileManager.ShutDownHook"));
}
public void shutDown() {
executor.shutdown();
try {
executor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void writeToDisc(String json, String fileName) {
executor.execute(() -> {
File jsonFile = new File(Paths.get(dir.getAbsolutePath(), fileName + ".json").toString());
File tempFile = null;
PrintWriter printWriter = null;
try {
tempFile = File.createTempFile("temp", null, dir);
if (!executor.isShutdown() && !executor.isTerminated() && !executor.isTerminating())
tempFile.deleteOnExit();
printWriter = new PrintWriter(tempFile);
printWriter.println(json);
if (Utilities.isWindows()) {
// Work around an issue on Windows whereby you can't rename over existing files.
final File canonical = jsonFile.getCanonicalFile();
if (canonical.exists() && !canonical.delete()) {
throw new IOException("Failed to delete canonical file for replacement with save");
}
if (!tempFile.renameTo(canonical)) {
throw new IOException("Failed to rename " + tempFile + " to " + canonical);
}
} else if (!tempFile.renameTo(jsonFile)) {
throw new IOException("Failed to rename " + tempFile + " to " + jsonFile);
}
} catch (Throwable t) {
log.error("storageFile " + jsonFile.toString());
t.printStackTrace();
} finally {
if (tempFile != null && tempFile.exists()) {
log.warn("Temp file still exists after failed save. We will delete it now. storageFile=" + fileName);
if (!tempFile.delete())
log.error("Cannot delete temp file.");
}
if (printWriter != null)
printWriter.close();
}
});
}
}

View file

@ -22,7 +22,7 @@ import io.bisq.common.app.AppModule;
import io.bisq.core.dao.blockchain.BsqBlockchainManager;
import io.bisq.core.dao.blockchain.BsqFullNode;
import io.bisq.core.dao.blockchain.BsqLiteNode;
import io.bisq.core.dao.blockchain.json.JsonExporter;
import io.bisq.core.dao.blockchain.json.DaoJsonExporter;
import io.bisq.core.dao.blockchain.parse.*;
import io.bisq.core.dao.compensation.CompensationRequestManager;
import io.bisq.core.dao.compensation.CompensationRequestModel;
@ -60,7 +60,7 @@ public class DaoModule extends AppModule {
bind(VotingVerification.class).in(Singleton.class);
bind(IssuanceVerification.class).in(Singleton.class);
bind(JsonExporter.class).in(Singleton.class);
bind(DaoJsonExporter.class).in(Singleton.class);
bind(DaoPeriodService.class).in(Singleton.class);
bind(VotingService.class).in(Singleton.class);

View file

@ -26,7 +26,7 @@ import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.network.Msg;
import io.bisq.common.util.Utilities;
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
import io.bisq.core.dao.blockchain.json.JsonExporter;
import io.bisq.core.dao.blockchain.json.DaoJsonExporter;
import io.bisq.core.dao.blockchain.p2p.GetBsqBlocksRequest;
import io.bisq.core.dao.blockchain.p2p.GetBsqBlocksResponse;
import io.bisq.core.dao.blockchain.p2p.NewBsqBlockBroadcastMsg;
@ -47,7 +47,7 @@ import org.jetbrains.annotations.NotNull;
public class BsqFullNode extends BsqNode {
private BsqFullNodeExecutor bsqFullNodeExecutor;
private final JsonExporter jsonExporter;
private final DaoJsonExporter daoJsonExporter;
@Getter
private boolean parseBlockchainComplete;
@ -62,14 +62,14 @@ public class BsqFullNode extends BsqNode {
BsqParser bsqParser,
BsqFullNodeExecutor bsqFullNodeExecutor,
BsqChainState bsqChainState,
JsonExporter jsonExporter,
DaoJsonExporter daoJsonExporter,
FeeService feeService) {
super(p2PService,
bsqParser,
bsqChainState,
feeService);
this.bsqFullNodeExecutor = bsqFullNodeExecutor;
this.jsonExporter = jsonExporter;
this.daoJsonExporter = daoJsonExporter;
}
@ -187,7 +187,7 @@ public class BsqFullNode extends BsqNode {
@Override
protected void onNewBsqBlock(BsqBlock bsqBlock) {
super.onNewBsqBlock(bsqBlock);
jsonExporter.maybeExport();
daoJsonExporter.maybeExport();
if (parseBlockchainComplete && p2pNetworkReady)
publishNewBlock(bsqBlock);
}

View file

@ -0,0 +1,221 @@
/*
* 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 io.bisq.core.dao.blockchain.json;
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 com.google.inject.Inject;
import io.bisq.common.storage.JsonFileManager;
import io.bisq.common.storage.Storage;
import io.bisq.common.util.Utilities;
import io.bisq.core.dao.DaoOptionKeys;
import io.bisq.core.dao.blockchain.parse.BsqChainState;
import io.bisq.core.dao.blockchain.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import javax.inject.Named;
import java.io.File;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
public class DaoJsonExporter {
private final boolean dumpBlockchainData;
private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", 1, 1, 1200);
private File txDir, txOutputDir, bsqChainStateDir;
private JsonFileManager txFileManager, txOutputFileManager, bsqChainStateFileManager;
private BsqChainState bsqChainState;
@Inject
public DaoJsonExporter(BsqChainState bsqChainState,
@Named(Storage.STORAGE_DIR) File storageDir,
@Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
this.bsqChainState = bsqChainState;
this.dumpBlockchainData = dumpBlockchainData;
if (dumpBlockchainData) {
txDir = new File(Paths.get(storageDir.getAbsolutePath(), "tx").toString());
if (!txDir.exists())
if (!txDir.mkdir())
log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath());
txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "txo").toString());
if (!txOutputDir.exists())
if (!txOutputDir.mkdir())
log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath());
bsqChainStateDir = new File(Paths.get(storageDir.getAbsolutePath(), "all").toString());
if (!bsqChainStateDir.exists())
if (!bsqChainStateDir.mkdir())
log.warn("make bsqChainStateDir failed.\nbsqChainStateDir=" + bsqChainStateDir.getAbsolutePath());
txFileManager = new JsonFileManager(txDir);
txOutputFileManager = new JsonFileManager(txOutputDir);
bsqChainStateFileManager = new JsonFileManager(bsqChainStateDir);
}
}
public void shutDown() {
txFileManager.shutDown();
txOutputFileManager.shutDown();
bsqChainStateFileManager.shutDown();
}
public void maybeExport() {
if (dumpBlockchainData) {
/* List<TxOutputForJson> list = bsqChainState.getVerifiedTxOutputSet().stream()
.map(this::getTxOutputForJson)
.collect(Collectors.toList());
list.sort((o1, o2) -> (o1.getSortData().compareTo(o2.getSortData())));
TxOutputForJson[] array = new TxOutputForJson[list.size()];
list.toArray(array);*/
//jsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(array)), 5000);
ListenableFuture<Void> future = executor.submit(() -> {
final BsqChainState bsqChainStateClone = bsqChainState.getClone();
Map<String, Long> burntFeeByTxIdMap = bsqChainStateClone.getBurntFeeByTxIdMap();
Map<TxIdIndexTuple, SpentInfo> spentInfoByTxOutputMap = bsqChainStateClone.getSpentInfoByTxOutputMap();
Set<TxOutput> unspentTxOutputSet = bsqChainStateClone.getUnspentTxOutputSet();
Set<TxOutput> compensationRequestOpReturnTxOutputs = bsqChainStateClone.getCompensationRequestOpReturnTxOutputs();
Set<TxOutput> votingTxOutputs = bsqChainStateClone.getVotingTxOutputs();
Set<TxOutput> invalidTxOutputs = bsqChainStateClone.getInvalidatedTxOutputs();
Set<TxOutput> issuanceTxOutputSet = bsqChainStateClone.getIssuanceBtcTxOutputsByBtcAddressMap()
.values()
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toSet());
String genesisTxId = bsqChainStateClone.getGenesisTxId();
// map all txs to json txs
List<TxForJson> txs = new ArrayList<>();
for (Tx tx : bsqChainStateClone.getTxMap().values()) {
String txId = tx.getId();
int blockHeight = tx.getBlockHeight();
String blockHash = tx.getBlockHash();
boolean isGenesisTx = tx.isGenesisTx();
//boolean isUnspent = false;
boolean isVote = false;
boolean isCompensationRequest = false;
boolean hasBurnedFee = burntFeeByTxIdMap.containsKey(txId);
boolean isIssuance = false;
String txType = TxTypeForJson.UNDEFINED.getDisplayString();
boolean hasAnyInvalidTxOutput = false;
List<TxOutputForJson> outputs = new ArrayList<>();
for (TxOutput txOutput : tx.getOutputs()) {
// isUnspent = isUnspent || unspentTxOutputSet.contains(txOutput);
isVote = isVote || votingTxOutputs.contains(txOutput);
isCompensationRequest = isCompensationRequest || compensationRequestOpReturnTxOutputs.contains(txOutput);
isIssuance = isIssuance || issuanceTxOutputSet.contains(txOutput);
int outputIndex = txOutput.getIndex();
final long bsqAmount = -1;//txOutput.getValue();
//TODO
final long btcAmount = -1;
final int height = txOutput.getBlockHeight();
final boolean verified = !invalidTxOutputs.contains(txOutput);
hasAnyInvalidTxOutput = !verified || hasAnyInvalidTxOutput;
final long burntFee = burntFeeByTxIdMap.containsKey(txId) ? burntFeeByTxIdMap.get(txId) : 0;
final ScriptPubKeyForJson scriptPubKey = new ScriptPubKeyForJson(txOutput.getPubKeyScript());
SpentInfoForJson spentInfoJson = spentInfoByTxOutputMap.containsKey(txOutput.getTxIdIndexTuple()) ?
new SpentInfoForJson(spentInfoByTxOutputMap.get(txOutput.getTxIdIndexTuple())) : null;
final long time = txOutput.getTime();
outputs.add(new TxOutputForJson(txId,
outputIndex,
bsqAmount,
btcAmount,
height,
verified,
burntFee,
scriptPubKey,
spentInfoJson,
time
));
}
// after iteration of all txOutputs we can evaluate the txType
if (txId.equals(genesisTxId))
txType = TxTypeForJson.GENESIS.getDisplayString();
else if (isVote)
txType = TxTypeForJson.VOTE.getDisplayString();
else if (isCompensationRequest)
txType = TxTypeForJson.COMPENSATION_REQUEST.getDisplayString();
else if (hasBurnedFee) // burned fee contains also vote and comp request but we detect those cases above
txType = TxTypeForJson.PAY_TRADE_FEE.getDisplayString();
else if (isIssuance)
txType = TxTypeForJson.ISSUANCE.getDisplayString();
else
txType = TxTypeForJson.SEND_BSQ.getDisplayString();
List<TxInputForJson> inputs = new ArrayList<>();
for (TxInput txInput : tx.getInputs()) {
int spendingTxOutputIndex = txInput.getSpendingTxOutputIndex();
String spendingTxId = txInput.getSpendingTxId();
//TODO
long bsqAmount = -1;
boolean isVerified = !hasAnyInvalidTxOutput;
inputs.add(new TxInputForJson(spendingTxOutputIndex,
spendingTxId,
bsqAmount,
isVerified));
}
// we evaluated during the tx loop so we apply txType here at the end again to have all
// txOutputs the same txType.
final String finalTxType = txType;
outputs.stream().forEach(txOutputForJson -> {
txOutputForJson.setTxType(finalTxType);
txOutputFileManager.writeToDisc(Utilities.objectToJson(txOutputForJson), txOutputForJson.getId());
});
final TxForJson txForJson = new TxForJson(txId,
blockHeight,
blockHash,
inputs,
outputs,
isGenesisTx,
txType);
txs.add(txForJson);
txFileManager.writeToDisc(Utilities.objectToJson(txForJson), txId);
}
bsqChainStateFileManager.writeToDisc(Utilities.objectToJson(bsqChainStateClone), "bsqChainState");
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();
}
});
}
}
}

View file

@ -1,152 +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 io.bisq.core.dao.blockchain.json;
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 com.google.inject.Inject;
import io.bisq.common.storage.PlainTextWrapper;
import io.bisq.common.storage.Storage;
import io.bisq.common.util.Utilities;
import io.bisq.core.dao.DaoOptionKeys;
import io.bisq.core.dao.blockchain.btcd.PubKeyScript;
import io.bisq.core.dao.blockchain.parse.BsqChainState;
import io.bisq.core.dao.blockchain.vo.SpentInfo;
import io.bisq.core.dao.blockchain.vo.TxOutput;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import javax.inject.Named;
import java.io.File;
@Slf4j
public class JsonExporter {
private final Storage<PlainTextWrapper> jsonStorage;
private final boolean dumpBlockchainData;
private final File storageDir;
private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", 1, 1, 1200);
private BsqChainState bsqChainState;
@Inject
public JsonExporter(Storage<PlainTextWrapper> jsonStorage,
BsqChainState bsqChainState,
@Named(Storage.STORAGE_DIR) File storageDir,
@Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
this.bsqChainState = bsqChainState;
this.storageDir = storageDir;
this.jsonStorage = jsonStorage;
this.dumpBlockchainData = dumpBlockchainData;
if (dumpBlockchainData)
this.jsonStorage.initWithFileName("bsqChainState.json");
}
public void maybeExport() {
if (dumpBlockchainData) {
/* List<TxOutputForJson> list = bsqChainState.getVerifiedTxOutputSet().stream()
.map(this::getTxOutputForJson)
.collect(Collectors.toList());
list.sort((o1, o2) -> (o1.getSortData().compareTo(o2.getSortData())));
TxOutputForJson[] array = new TxOutputForJson[list.size()];
list.toArray(array);*/
//jsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(array)), 5000);
ListenableFuture<Void> future = executor.submit(() -> {
jsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(bsqChainState.getClone())), 5000);
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();
}
});
// keep the individual file storage option as code as we dont know yet what we will use.
/* log.error("txOutputForJson " + txOutputForJson);
File txoDir = new File(Paths.get(storageDir.getAbsolutePath(), "txo").toString());
if (!txoDir.exists())
if (!txoDir.mkdir())
log.warn("make txoDir failed.\ntxoDir=" + txoDir.getAbsolutePath());
File txoFile = new File(Paths.get(txoDir.getAbsolutePath(),
txOutput.getTxId() + ":" + outputIndex + ".json").toString());
// Nr of write requests might be a bit heavy, consider write whole list to one file
FileManager<PlainTextWrapper> fileManager = new FileManager<>(storageDir, txoFile, 1);
fileManager.saveLater(new PlainTextWrapper(Utilities.objectToJson(txOutputForJson)));*/
}
}
private TxOutputForJson getTxOutputForJson(TxOutput txOutput) {
String txId = txOutput.getTxId();
int outputIndex = txOutput.getIndex();
final long bsqAmount = txOutput.getValue();
final int height = txOutput.getBlockHeight();
/* final boolean isBsqCoinBase = txOutput.isIssuanceOutput();
final boolean verified = txOutput.isVerified();
final long burntFee = txOutput.getBurntFee();
final long btcTxFee = txOutput.getBtcTxFee();*/
// TODO
final boolean isBsqCoinBase = false;
final boolean verified = true;
final long burntFee = 0;
final long btcTxFee = 0;
PubKeyScript pubKeyScript = txOutput.getPubKeyScript();
final ScriptPubKeyForJson scriptPubKey = new ScriptPubKeyForJson(pubKeyScript.getAddresses(),
pubKeyScript.getAsm(),
pubKeyScript.getHex(),
pubKeyScript.getReqSigs(),
pubKeyScript.getType().toString());
SpentInfoForJson spentInfoJson = null;
// SpentInfo spentInfo = txOutput.getSpentInfo();
SpentInfo spentInfo = null;
if (spentInfo != null)
spentInfoJson = new SpentInfoForJson(spentInfo.getBlockHeight(),
spentInfo.getInputIndex(),
spentInfo.getTxId());
final long time = txOutput.getTime();
final String txVersion = "";//txOutput.getTxVersion();
return new TxOutputForJson(txId,
outputIndex,
bsqAmount,
height,
isBsqCoinBase,
verified,
burntFee,
btcTxFee,
scriptPubKey,
spentInfoJson,
time,
txVersion
);
}
}

View file

@ -17,6 +17,7 @@
package io.bisq.core.dao.blockchain.json;
import io.bisq.core.dao.blockchain.btcd.PubKeyScript;
import lombok.Value;
import java.util.List;
@ -28,4 +29,12 @@ public class ScriptPubKeyForJson {
private final String hex;
private final int reqSigs;
private final String type;
public ScriptPubKeyForJson(PubKeyScript pubKeyScript) {
addresses = pubKeyScript.getAddresses();
asm = pubKeyScript.getAsm();
hex = pubKeyScript.getHex();
reqSigs = pubKeyScript.getReqSigs();
type = pubKeyScript.getType().toString();
}
}

View file

@ -17,6 +17,7 @@
package io.bisq.core.dao.blockchain.json;
import io.bisq.core.dao.blockchain.vo.SpentInfo;
import lombok.Value;
@Value
@ -24,4 +25,10 @@ public class SpentInfoForJson {
private final long height;
private final int inputIndex;
private final String txId;
public SpentInfoForJson(SpentInfo spentInfo) {
height = spentInfo.getBlockHeight();
inputIndex = spentInfo.getInputIndex();
txId = spentInfo.getTxId();
}
}

View 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 io.bisq.core.dao.blockchain.json;
import io.bisq.common.app.Version;
import lombok.Value;
import java.util.List;
@Value
public class TxForJson {
private final String txVersion = Version.BSQ_TX_VERSION;
private final String id;
private final int blockHeight;
private final String blockHash;
private final List<TxInputForJson> inputs;
private final List<TxOutputForJson> outputs;
private final boolean isGenesisTx;
private final String txType;
}

View 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 io.bisq.core.dao.blockchain.json;
import lombok.Value;
import javax.annotation.concurrent.Immutable;
@Value
@Immutable
public class TxInputForJson {
private final int spendingTxOutputIndex;
private final String spendingTxId;
private final long bsqAmount;
private final boolean isVerified;
}

View file

@ -17,25 +17,27 @@
package io.bisq.core.dao.blockchain.json;
import lombok.Value;
import io.bisq.common.app.Version;
import lombok.Data;
import lombok.Setter;
@Value
@Data
public class TxOutputForJson {
private final String txVersion = Version.BSQ_TX_VERSION;
private final String txId;
private final int outputIndex;
private final long bsqAmount;
private final long btcAmount;
private final int height;
private final boolean isBsqCoinBase;
private final boolean isVerified;
private final long burntFee;
private final long btcTxFee;
private final ScriptPubKeyForJson scriptPubKey;
private final SpentInfoForJson spentInfo;
private final long time;
private final String txVersion;
@Setter
private String txType;
public String getSortData() {
return height + txId + outputIndex;
public String getId() {
return txId + ":" + outputIndex;
}
}

View file

@ -0,0 +1,37 @@
/*
* 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 io.bisq.core.dao.blockchain.json;
import lombok.Getter;
public enum TxTypeForJson {
UNDEFINED("Undefined"),
GENESIS("Genesis"),
SEND_BSQ("Send BSQ"),
PAY_TRADE_FEE("Pay trade fee"),
COMPENSATION_REQUEST("Compensation request"),
VOTE("Vote"),
ISSUANCE("Issuance");
@Getter
private String displayString;
TxTypeForJson(String displayString) {
this.displayString = displayString;
}
}

View file

@ -35,6 +35,8 @@ import lombok.extern.slf4j.Slf4j;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.*;
import java.util.stream.Collectors;
@ -100,8 +102,9 @@ public class BsqChainState implements Persistable {
private final Set<Tuple2<Long, Integer>> compensationRequestFees = new HashSet<>();
private final Set<Tuple2<Long, Integer>> votingFees = new HashSet<>();
private final Set<TxOutput> compensationRequestOpReturnTxOutputs = new HashSet<>();
private final Set<String> compensationRequestBtcAddresses = new HashSet<>();
private final Set<TxOutput> votingTxOutputs = new HashSet<>();
private final Set<TxOutput> invalidatedTxOutputs = new HashSet<>();
private final Set<String> compensationRequestBtcAddresses = new HashSet<>();
private final Map<String, Set<TxOutput>> issuanceBtcTxOutputsByBtcAddressMap = new HashMap<>();
private final String genesisTxId;
@ -113,7 +116,7 @@ public class BsqChainState implements Persistable {
transient private final boolean dumpBlockchainData;
transient private final Storage<BsqChainState> snapshotBsqChainStateStorage;
transient private BsqChainState snapshotCandidate;
transient private final FunctionalReadWriteLock lock;
transient private FunctionalReadWriteLock lock;
///////////////////////////////////////////////////////////////////////////////////////////
@ -144,6 +147,10 @@ public class BsqChainState implements Persistable {
lock = new FunctionalReadWriteLock(true);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
lock = new FunctionalReadWriteLock(true);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public write access
@ -263,9 +270,9 @@ public class BsqChainState implements Persistable {
});
}
void addCompensationRequestOpReturnOutput(TxOutput opReturnTxOutput) {
void addCompensationRequestOpReturnOutput(TxOutput txOutput) {
lock.write(() -> {
compensationRequestOpReturnTxOutputs.add(opReturnTxOutput);
compensationRequestOpReturnTxOutputs.add(txOutput);
});
}
@ -275,9 +282,15 @@ public class BsqChainState implements Persistable {
});
}
void addVotingOpReturnOutput(TxOutput opReturnTxOutput) {
void addVotingOpReturnOutput(TxOutput txOutput) {
lock.write(() -> {
votingTxOutputs.add(opReturnTxOutput);
votingTxOutputs.add(txOutput);
});
}
void addInvalidatedTxOutputs(TxOutput txOutput) {
lock.write(() -> {
invalidatedTxOutputs.add(txOutput);
});
}
@ -351,6 +364,55 @@ public class BsqChainState implements Persistable {
});
}
// Only used for Json Exporter
public Map<String, Tx> getTxMap() {
return lock.read(() -> {
return txMap;
});
}
public Set<TxOutput> getVotingTxOutputs() {
return lock.read(() -> {
return votingTxOutputs;
});
}
public Set<TxOutput> getInvalidatedTxOutputs() {
return lock.read(() -> {
return invalidatedTxOutputs;
});
}
public Map<String, Set<TxOutput>> getIssuanceBtcTxOutputsByBtcAddressMap() {
return lock.read(() -> {
return issuanceBtcTxOutputsByBtcAddressMap;
});
}
public Set<TxOutput> getCompensationRequestOpReturnTxOutputs() {
return lock.read(() -> {
return compensationRequestOpReturnTxOutputs;
});
}
public Map<TxIdIndexTuple, SpentInfo> getSpentInfoByTxOutputMap() {
return lock.read(() -> {
return spentInfoByTxOutputMap;
});
}
public Map<String, Long> getBurntFeeByTxIdMap() {
return lock.read(() -> {
return burntFeeByTxIdMap;
});
}
public Set<TxOutput> getUnspentTxOutputSet() {
return lock.read(() -> {
return unspentTxOutputSet;
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Package scope read access

View file

@ -289,7 +289,7 @@ public class BsqParser {
if (availableValue >= txOutputValue && txOutputValue != 0) {
// We are spending available tokens
bsqChainState.addUnspentTxOutput(txOutput);
availableValue -= txOutputValue;
if (availableValue == 0) {
log.debug("We don't have anymore BSQ to spend");

View file

@ -39,7 +39,6 @@ public class TxOutput implements Persistable {
private final String txId;
private final PubKeyScript pubKeyScript;
@Nullable
@JsonExclude
private final String address;
@Nullable
@JsonExclude
@ -47,36 +46,10 @@ public class TxOutput implements Persistable {
private final int blockHeight;
private final long time;
/* public String getAddress() {
String address = "";
// Only at raw MS outputs addresses have more then 1 entry
// We do not support raw MS for BSQ but lets see if is needed anyway, might be removed
final List<String> addresses = pubKeyScript.getAddresses();
if (addresses.size() == 1) {
address = addresses.get(0);
} else if (addresses.size() > 1) {
final String msg = "We got a raw Multisig script. That is not supported for BSQ tokens.";
log.warn(msg);
address = addresses.toString();
if (DevEnv.DEV_MODE)
throw new RuntimeException(msg);
} else {
final String msg = "We got no address. Unsupported pubKeyScript";
log.warn(msg);
if (DevEnv.DEV_MODE)
throw new RuntimeException(msg);
}
return address;
}*/
public String getId() {
return txId + ":" + index;
}
public String getSortString() {
return blockHeight + ":" + txId;
}
public TxIdIndexTuple getTxIdIndexTuple() {
return new TxIdIndexTuple(txId, index);
}
@ -88,8 +61,9 @@ public class TxOutput implements Persistable {
",\n value=" + value +
",\n txId='" + txId + '\'' +
",\n pubKeyScript=" + pubKeyScript +
",\n blockHeight=" + blockHeight +
",\n address='" + address + '\'' +
",\n opReturnData=" + (opReturnData != null ? Utils.HEX.encode(opReturnData) : "null") +
",\n blockHeight=" + blockHeight +
",\n time=" + time +
"\n}";
}

View file

@ -37,6 +37,7 @@ import io.bisq.core.app.AppOptionKeys;
import io.bisq.core.app.BisqEnvironment;
import io.bisq.core.arbitration.ArbitratorManager;
import io.bisq.core.btc.wallet.*;
import io.bisq.core.dao.blockchain.json.DaoJsonExporter;
import io.bisq.core.filter.FilterManager;
import io.bisq.core.offer.OpenOfferManager;
import io.bisq.core.trade.TradeManager;
@ -409,6 +410,7 @@ public class BisqApp extends Application {
if (injector != null) {
injector.getInstance(ArbitratorManager.class).shutDown();
injector.getInstance(TradeManager.class).shutDown();
injector.getInstance(DaoJsonExporter.class).shutDown();
//noinspection CodeBlock2Expr
injector.getInstance(OpenOfferManager.class).shutDown(() -> {
injector.getInstance(P2PService.class).shutDown(() -> {