mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 14:42:37 +01:00
Extract snapshot code from BsqBlockChain to SnapshotManager. Rename *Verification classes to *Controller. Add BsqBlockController.
This commit is contained in:
parent
e55bba63fb
commit
c611eed0a6
28 changed files with 635 additions and 442 deletions
|
@ -63,21 +63,21 @@ public class DaoModule extends AppModule {
|
|||
bind(FullNode.class).in(Singleton.class);
|
||||
bind(BsqNodeProvider.class).in(Singleton.class);
|
||||
bind(BsqBlockChain.class).in(Singleton.class);
|
||||
bind(ReadModel.class).in(Singleton.class);
|
||||
bind(WriteModel.class).in(Singleton.class);
|
||||
bind(BsqBlockChainReadModel.class).in(Singleton.class);
|
||||
bind(BsqBlockChainWriteModel.class).in(Singleton.class);
|
||||
bind(SnapshotManager.class).in(Singleton.class);
|
||||
bind(BsqBlockChainChangeDispatcher.class).in(Singleton.class);
|
||||
|
||||
bind(GenesisTxVerification.class).in(Singleton.class);
|
||||
bind(BsqTxVerification.class).in(Singleton.class);
|
||||
bind(TxInputsVerification.class).in(Singleton.class);
|
||||
bind(TxInputVerification.class).in(Singleton.class);
|
||||
bind(TxOutputsVerification.class).in(Singleton.class);
|
||||
bind(TxOutputVerification.class).in(Singleton.class);
|
||||
bind(OpReturnVerification.class).in(Singleton.class);
|
||||
bind(CompensationRequestVerification.class).in(Singleton.class);
|
||||
bind(VotingVerification.class).in(Singleton.class);
|
||||
bind(IssuanceVerification.class).in(Singleton.class);
|
||||
bind(GenesisTxController.class).in(Singleton.class);
|
||||
bind(BsqTxController.class).in(Singleton.class);
|
||||
bind(TxInputsController.class).in(Singleton.class);
|
||||
bind(TxInputController.class).in(Singleton.class);
|
||||
bind(TxOutputsController.class).in(Singleton.class);
|
||||
bind(TxOutputController.class).in(Singleton.class);
|
||||
bind(OpReturnController.class).in(Singleton.class);
|
||||
bind(CompensationRequestController.class).in(Singleton.class);
|
||||
bind(VotingController.class).in(Singleton.class);
|
||||
bind(IssuanceController.class).in(Singleton.class);
|
||||
|
||||
bind(JsonBlockChainExporter.class).in(Singleton.class);
|
||||
bind(DaoPeriodService.class).in(Singleton.class);
|
||||
|
|
|
@ -20,35 +20,40 @@ package io.bisq.core.dao.blockchain;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.common.proto.persistable.PersistableEnvelope;
|
||||
import io.bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import io.bisq.common.storage.Storage;
|
||||
import io.bisq.common.util.FunctionalReadWriteLock;
|
||||
import io.bisq.common.util.Tuple2;
|
||||
import io.bisq.core.dao.DaoOptionKeys;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutput;
|
||||
import io.bisq.core.dao.blockchain.vo.TxType;
|
||||
import io.bisq.core.dao.blockchain.vo.util.TxIdIndexTuple;
|
||||
import io.bisq.generated.protobuffer.PB;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
// Represents mutable state of BSQ blocks
|
||||
// We get accessed the data from different threads so we need to make sure it is thread safe.
|
||||
|
||||
/**
|
||||
* Mutual state of the BSQ blockchain data.
|
||||
* <p>
|
||||
* We only have one thread which is writing data (ParseBlock or ParseBlocks thread from the lite node or full node executors).
|
||||
* Based on that limited requirement our threading model can be relaxed.
|
||||
* We use ReentrantReadWriteLock in a functional style.
|
||||
* <p>
|
||||
* We limit the access to BsqBlockChain to package private scope and only the BsqBlockChainReadModel and
|
||||
* BsqBlockChainWriteModel have access to have better overview and control about access.
|
||||
*/
|
||||
@Slf4j
|
||||
public class BsqBlockChain implements PersistableEnvelope {
|
||||
|
||||
|
@ -66,6 +71,16 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
return height % grid == 0 && height >= getSnapshotHeight(genesisHeight, height, grid);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Listener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public interface Listener {
|
||||
void onBlockAdded(BsqBlock bsqBlock);
|
||||
}
|
||||
|
||||
|
||||
private static final int SNAPSHOT_GRID = 100; // set high to deactivate
|
||||
private static final int ISSUANCE_MATURITY = 144 * 30; // 30 days
|
||||
private static final Coin GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5");
|
||||
|
@ -86,26 +101,24 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
// Instance fields
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Persisted data
|
||||
private final String genesisTxId;
|
||||
private final int genesisBlockHeight;
|
||||
|
||||
private final LinkedList<BsqBlock> bsqBlocks;
|
||||
private final Map<String, Tx> txMap;
|
||||
private final Map<TxIdIndexTuple, TxOutput> unspentTxOutputsMap;
|
||||
private final String genesisTxId;
|
||||
private final int genesisBlockHeight;
|
||||
private int chainHeadHeight = 0;
|
||||
@Nullable
|
||||
@Getter
|
||||
private Tx genesisTx;
|
||||
|
||||
// not impl in PB yet
|
||||
private final Set<Tuple2<Long, Integer>> compensationRequestFees;
|
||||
private final Set<Tuple2<Long, Integer>> votingFees;
|
||||
|
||||
// transient
|
||||
private final List<Listener> listeners = new ArrayList<>();
|
||||
|
||||
private int chainHeadHeight = 0;
|
||||
|
||||
@Nullable
|
||||
transient private Storage<BsqBlockChain> storage;
|
||||
@Nullable
|
||||
transient private BsqBlockChain snapshotCandidate;
|
||||
private Tx genesisTx;
|
||||
|
||||
transient private final FunctionalReadWriteLock lock;
|
||||
|
||||
|
||||
|
@ -115,14 +128,11 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@Inject
|
||||
public BsqBlockChain(PersistenceProtoResolver persistenceProtoResolver,
|
||||
@Named(Storage.STORAGE_DIR) File storageDir,
|
||||
@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId,
|
||||
public BsqBlockChain(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId,
|
||||
@Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) int genesisBlockHeight) {
|
||||
this.genesisTxId = genesisTxId;
|
||||
this.genesisBlockHeight = genesisBlockHeight;
|
||||
|
||||
storage = new Storage<>(storageDir, persistenceProtoResolver);
|
||||
|
||||
bsqBlocks = new LinkedList<>();
|
||||
txMap = new HashMap<>();
|
||||
|
@ -155,7 +165,7 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
|
||||
lock = new FunctionalReadWriteLock(true);
|
||||
|
||||
// not impl yet in PB
|
||||
// TODO not impl yet in PB
|
||||
compensationRequestFees = new HashSet<>();
|
||||
votingFees = new HashSet<>();
|
||||
}
|
||||
|
@ -201,92 +211,79 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Atomic access
|
||||
// Listeners
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public <T> T callFunctionWithWriteLock(Supplier<T> supplier) {
|
||||
return lock.write(supplier);
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
|
||||
//*****************************************************************************************
|
||||
//*****************************************************************************************
|
||||
// WRITE ACCESS
|
||||
//*****************************************************************************************
|
||||
//*****************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public write access
|
||||
// Write access: BsqBlockChain
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void applySnapshot() {
|
||||
void applySnapshot(BsqBlockChain snapshot) {
|
||||
lock.write(() -> {
|
||||
checkNotNull(storage, "storage must not be null");
|
||||
BsqBlockChain snapshot = storage.initAndGetPersistedWithFileName("BsqBlockChain", 100);
|
||||
bsqBlocks.clear();
|
||||
bsqBlocks.addAll(snapshot.bsqBlocks);
|
||||
|
||||
txMap.clear();
|
||||
txMap.putAll(snapshot.txMap);
|
||||
|
||||
unspentTxOutputsMap.clear();
|
||||
chainHeadHeight = 0;
|
||||
genesisTx = null;
|
||||
unspentTxOutputsMap.putAll(snapshot.unspentTxOutputsMap);
|
||||
|
||||
if (snapshot != null) {
|
||||
log.info("applySnapshot snapshot.chainHeadHeight=" + snapshot.chainHeadHeight);
|
||||
bsqBlocks.addAll(snapshot.bsqBlocks);
|
||||
txMap.putAll(snapshot.txMap);
|
||||
unspentTxOutputsMap.putAll(snapshot.unspentTxOutputsMap);
|
||||
chainHeadHeight = snapshot.chainHeadHeight;
|
||||
genesisTx = snapshot.genesisTx;
|
||||
} else {
|
||||
log.info("Try to apply snapshot but no stored snapshot available");
|
||||
}
|
||||
|
||||
printDetails();
|
||||
chainHeadHeight = snapshot.chainHeadHeight;
|
||||
genesisTx = snapshot.genesisTx;
|
||||
});
|
||||
}
|
||||
|
||||
void setCreateCompensationRequestFee(long fee, int blockHeight) {
|
||||
lock.write(() -> compensationRequestFees.add(new Tuple2<>(fee, blockHeight)));
|
||||
}
|
||||
|
||||
void setVotingFee(long fee, int blockHeight) {
|
||||
lock.write(() -> votingFees.add(new Tuple2<>(fee, blockHeight)));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Write access: BsqBlock
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void addBlock(BsqBlock bsqBlock) {
|
||||
lock.write(() -> {
|
||||
bsqBlocks.add(bsqBlock);
|
||||
bsqBlock.getTxs().forEach(BsqBlockChain.this::addTxToMap);
|
||||
chainHeadHeight = bsqBlock.getHeight();
|
||||
printDetails();
|
||||
|
||||
listeners.forEach(l -> l.onBlockAdded(bsqBlock));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Package scope write access
|
||||
// Write access: Tx
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//TODO refactor logic out
|
||||
void addBlock(BsqBlock block) throws BlockNotConnectingException {
|
||||
try {
|
||||
lock.write2(() -> {
|
||||
if (!bsqBlocks.contains(block)) {
|
||||
if (bsqBlocks.isEmpty() || (bsqBlocks.getLast().getHash().equals(block.getPreviousBlockHash()) &&
|
||||
bsqBlocks.getLast().getHeight() + 1 == block.getHeight())) {
|
||||
bsqBlocks.add(block);
|
||||
block.getTxs().forEach(BsqBlockChain.this::addTxToMap);
|
||||
chainHeadHeight = block.getHeight();
|
||||
maybeMakeSnapshot();
|
||||
printDetails();
|
||||
} else {
|
||||
log.warn("addBlock called with a not connecting block:\n" +
|
||||
"height()={}, hash()={}, head.height()={}, head.hash()={}",
|
||||
block.getHeight(), block.getHash(), bsqBlocks.getLast().getHeight(), bsqBlocks.getLast().getHash());
|
||||
throw new BlockNotConnectingException(block);
|
||||
}
|
||||
} else {
|
||||
log.trace("We got that block already");
|
||||
}
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new BlockNotConnectingException(block);
|
||||
} catch (Throwable e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
void setGenesisTx(Tx tx) {
|
||||
lock.write(() -> genesisTx = tx);
|
||||
}
|
||||
|
||||
void addTxToMap(Tx tx) {
|
||||
lock.write(() -> txMap.put(tx.getId(), tx));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Write access: TxOutput
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void addUnspentTxOutput(TxOutput txOutput) {
|
||||
lock.write(() -> {
|
||||
checkArgument(txOutput.isVerified(), "txOutput must be verified at addUnspentTxOutput");
|
||||
|
@ -298,35 +295,123 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
lock.write(() -> unspentTxOutputsMap.remove(txOutput.getTxIdIndexTuple()));
|
||||
}
|
||||
|
||||
void setGenesisTx(Tx tx) {
|
||||
lock.write(() -> genesisTx = tx);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public read access
|
||||
// Write access: Misc
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String getGenesisTxId() {
|
||||
return genesisTxId;
|
||||
void setCreateCompensationRequestFee(long fee, int blockHeight) {
|
||||
lock.write(() -> compensationRequestFees.add(new Tuple2<>(fee, blockHeight)));
|
||||
}
|
||||
|
||||
public int getGenesisBlockHeight() {
|
||||
return lock.read(() -> genesisBlockHeight);
|
||||
void setVotingFee(long fee, int blockHeight) {
|
||||
lock.write(() -> votingFees.add(new Tuple2<>(fee, blockHeight)));
|
||||
}
|
||||
|
||||
public BsqBlockChain getClone() {
|
||||
return getClone(this);
|
||||
|
||||
//*****************************************************************************************
|
||||
//*****************************************************************************************
|
||||
// READ ACCESS
|
||||
//*****************************************************************************************
|
||||
//*****************************************************************************************
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Read access: BsqBlockChain
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BsqBlockChain getClone() {
|
||||
return lock.read(() -> getClone(this));
|
||||
}
|
||||
|
||||
private BsqBlockChain getClone(BsqBlockChain bsqBlockChain) {
|
||||
BsqBlockChain getClone(BsqBlockChain bsqBlockChain) {
|
||||
return lock.read(() -> (BsqBlockChain) BsqBlockChain.fromProto(bsqBlockChain.getBsqBlockChainBuilder().build()));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Read access: BsqBlock
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LinkedList<BsqBlock> getBsqBlocks() {
|
||||
return lock.read(() -> bsqBlocks);
|
||||
}
|
||||
|
||||
boolean containsBlock(BsqBlock bsqBlock) {
|
||||
return lock.read(() -> bsqBlocks.contains(bsqBlock));
|
||||
}
|
||||
|
||||
public int getChainHeadHeight() {
|
||||
return chainHeadHeight;
|
||||
}
|
||||
|
||||
public int getGenesisBlockHeight() {
|
||||
return genesisBlockHeight;
|
||||
}
|
||||
|
||||
List<BsqBlock> getClonedBlocksFrom(int fromBlockHeight) {
|
||||
return lock.read(() -> {
|
||||
BsqBlockChain clone = getClone();
|
||||
List<BsqBlock> filtered = clone.bsqBlocks.stream()
|
||||
.filter(block -> block.getHeight() >= fromBlockHeight)
|
||||
.collect(Collectors.toList());
|
||||
filtered.forEach(BsqBlock::reset);
|
||||
return filtered;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Read access: Tx
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Optional<Tx> getTx(String txId) {
|
||||
return lock.read(() -> txMap.get(txId) != null ? Optional.of(txMap.get(txId)) : Optional.<Tx>empty());
|
||||
}
|
||||
|
||||
public Map<String, Tx> getTxMap() {
|
||||
return lock.read(() -> txMap);
|
||||
}
|
||||
|
||||
public Optional<Tx> findTx(String txId) {
|
||||
return lock.read(() -> {
|
||||
Tx tx = getTxMap().get(txId);
|
||||
if (tx != null)
|
||||
return Optional.of(tx);
|
||||
else
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
public Set<Tx> getTransactions() {
|
||||
return lock.read(() -> getTxMap().entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
public Set<Tx> getFeeTransactions() {
|
||||
return lock.read(() -> getTxMap().entrySet().stream().filter(e -> e.getValue().getBurntFee() > 0).map(Map.Entry::getValue).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
public boolean hasTxBurntFee(String txId) {
|
||||
return lock.read(() -> getTx(txId).map(Tx::getBurntFee).filter(fee -> fee > 0).isPresent());
|
||||
}
|
||||
|
||||
public boolean containsTx(String txId) {
|
||||
return lock.read(() -> getTx(txId).isPresent());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Tx getGenesisTx() {
|
||||
return genesisTx;
|
||||
}
|
||||
|
||||
public String getGenesisTxId() {
|
||||
return genesisTxId;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Read access: TxOutput
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Optional<TxOutput> getUnspentTxOutput(TxIdIndexTuple txIdIndexTuple) {
|
||||
return lock.read(() -> unspentTxOutputsMap.entrySet().stream()
|
||||
.filter(e -> e.getKey().equals(txIdIndexTuple))
|
||||
|
@ -337,55 +422,10 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
return lock.read(() -> getSpendableTxOutput(txId, index).isPresent());
|
||||
}
|
||||
|
||||
public boolean hasTxBurntFee(String txId) {
|
||||
return lock.read(() -> getTx(txId).map(Tx::getBurntFee).filter(fee -> fee > 0).isPresent());
|
||||
}
|
||||
|
||||
public Optional<TxType> getTxType(String txId) {
|
||||
return lock.read(() -> getTx(txId).map(Tx::getTxType));
|
||||
}
|
||||
|
||||
public boolean containsTx(String txId) {
|
||||
return lock.read(() -> getTx(txId).isPresent());
|
||||
}
|
||||
|
||||
public Optional<Tx> findTx(String txId) {
|
||||
Tx tx = getTxMap().get(txId);
|
||||
if (tx != null)
|
||||
return Optional.of(tx);
|
||||
else
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public int getChainHeadHeight() {
|
||||
return lock.read(() -> chainHeadHeight);
|
||||
}
|
||||
|
||||
public Map<String, Tx> getTxMap() {
|
||||
return lock.read(() -> txMap);
|
||||
}
|
||||
|
||||
List<BsqBlock> getResetBlocksFrom(int fromBlockHeight) {
|
||||
return lock.read(() -> {
|
||||
BsqBlockChain clone = getClone();
|
||||
List<BsqBlock> filtered = clone.bsqBlocks.stream()
|
||||
.filter(block -> block.getHeight() >= fromBlockHeight)
|
||||
.collect(Collectors.toList());
|
||||
filtered.forEach(BsqBlock::reset);
|
||||
return filtered;
|
||||
});
|
||||
}
|
||||
|
||||
public Coin getTotalBurntFee() {
|
||||
return lock.read(() -> Coin.valueOf(getTxMap().entrySet().stream().mapToLong(e -> e.getValue().getBurntFee()).sum()));
|
||||
}
|
||||
|
||||
public Set<Tx> getFeeTransactions() {
|
||||
return lock.read(() -> getTxMap().entrySet().stream().filter(e -> e.getValue().getBurntFee() > 0).map(Map.Entry::getValue).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
public Coin getIssuedAmount() {
|
||||
return lock.read(() -> BsqBlockChain.GENESIS_TOTAL_SUPPLY);
|
||||
private Set<TxOutput> getAllTxOutputs() {
|
||||
return txMap.values().stream()
|
||||
.flatMap(tx -> tx.getOutputs().stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Set<TxOutput> getUnspentTxOutputs() {
|
||||
|
@ -396,24 +436,43 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
return lock.read(() -> getAllTxOutputs().stream().filter(e -> e.isVerified() && !e.isUnspent()).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
public Set<Tx> getTransactions() {
|
||||
return lock.read(() -> getTxMap().entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Package scope read access
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Optional<TxOutput> getSpendableTxOutput(String txId, int index) {
|
||||
return lock.read(() -> getSpendableTxOutput(new TxIdIndexTuple(txId, index)));
|
||||
}
|
||||
|
||||
Optional<TxOutput> getSpendableTxOutput(TxIdIndexTuple txIdIndexTuple) {
|
||||
return lock.read(() -> getUnspentTxOutput(txIdIndexTuple)
|
||||
.filter(this::isTxOutputMature));
|
||||
}
|
||||
|
||||
private Optional<TxOutput> getSpendableTxOutput(String txId, int index) {
|
||||
return lock.read(() -> getSpendableTxOutput(new TxIdIndexTuple(txId, index)));
|
||||
}
|
||||
|
||||
//TODO
|
||||
// for genesis we don't need it and for issuance we need more implemented first
|
||||
private boolean isTxOutputMature(TxOutput spendingTxOutput) {
|
||||
return lock.read(() -> true);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Read access: TxType
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Optional<TxType> getTxType(String txId) {
|
||||
return lock.read(() -> getTx(txId).map(Tx::getTxType));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Read access: Misc
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Coin getTotalBurntFee() {
|
||||
return lock.read(() -> Coin.valueOf(getTxMap().entrySet().stream().mapToLong(e -> e.getValue().getBurntFee()).sum()));
|
||||
}
|
||||
|
||||
public Coin getIssuedAmount() {
|
||||
return lock.read(() -> BsqBlockChain.GENESIS_TOTAL_SUPPLY);
|
||||
}
|
||||
|
||||
long getCreateCompensationRequestFee(int blockHeight) {
|
||||
return lock.read(() -> {
|
||||
long fee = -1;
|
||||
|
@ -462,54 +521,7 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
//TODO
|
||||
// for genesis we don't need it and for issuance we need more implemented first
|
||||
private boolean isTxOutputMature(TxOutput spendingTxOutput) {
|
||||
return lock.read(() -> true);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Optional<Tx> getTx(String txId) {
|
||||
return lock.read(() -> txMap.get(txId) != null ? Optional.of(txMap.get(txId)) : Optional.<Tx>empty());
|
||||
}
|
||||
|
||||
private boolean isSnapshotHeight(int height) {
|
||||
return isSnapshotHeight(genesisBlockHeight, height, SNAPSHOT_GRID);
|
||||
}
|
||||
|
||||
private void maybeMakeSnapshot() {
|
||||
lock.read(() -> {
|
||||
if (isSnapshotHeight(getChainHeadHeight()) &&
|
||||
(snapshotCandidate == null ||
|
||||
snapshotCandidate.chainHeadHeight != getChainHeadHeight())) {
|
||||
// At trigger event we store the latest snapshotCandidate to disc
|
||||
if (snapshotCandidate != null) {
|
||||
// We clone because storage is in a threaded context
|
||||
final BsqBlockChain cloned = getClone(snapshotCandidate);
|
||||
checkNotNull(storage, "storage must not be null");
|
||||
storage.queueUpForSave(cloned);
|
||||
// don't access cloned anymore with methods as locks are transient!
|
||||
log.info("Saved snapshotCandidate to Disc at height " + cloned.chainHeadHeight);
|
||||
}
|
||||
// Now we clone and keep it in memory for the next trigger
|
||||
snapshotCandidate = getClone(this);
|
||||
// don't access cloned anymore with methods as locks are transient!
|
||||
log.debug("Cloned new snapshotCandidate at height " + snapshotCandidate.chainHeadHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<TxOutput> getAllTxOutputs() {
|
||||
return txMap.values().stream()
|
||||
.flatMap(tx -> tx.getOutputs().stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private void printDetails() {
|
||||
void printDetails() {
|
||||
log.debug("\nchainHeadHeight={}\n" +
|
||||
" blocks.size={}\n" +
|
||||
" txMap.size={}\n" +
|
||||
|
@ -523,5 +535,12 @@ public class BsqBlockChain implements PersistableEnvelope {
|
|||
compensationRequestFees.size(),
|
||||
votingFees.size());
|
||||
}
|
||||
|
||||
|
||||
// Probably not needed anymore
|
||||
public <T> T callFunctionWithWriteLock(Supplier<T> supplier) {
|
||||
return lock.write(supplier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import io.bisq.core.dao.blockchain.vo.util.TxIdIndexTuple;
|
|||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
@ -31,34 +32,44 @@ import java.util.Optional;
|
|||
/**
|
||||
* Encapsulates read access to BsqBlockChain.
|
||||
*/
|
||||
public class ReadModel {
|
||||
public class BsqBlockChainReadModel {
|
||||
private BsqBlockChain bsqBlockChain;
|
||||
|
||||
@Inject
|
||||
public ReadModel(BsqBlockChain bsqBlockChain) {
|
||||
public BsqBlockChainReadModel(BsqBlockChain bsqBlockChain) {
|
||||
this.bsqBlockChain = bsqBlockChain;
|
||||
}
|
||||
|
||||
public Optional<TxOutput> getSpendableTxOutput(TxIdIndexTuple txIdIndexTuple) {
|
||||
return bsqBlockChain.getSpendableTxOutput(txIdIndexTuple);
|
||||
}
|
||||
|
||||
public Optional<TxOutput> getSpendableTxOutput(String txId, int index) {
|
||||
return getSpendableTxOutput(new TxIdIndexTuple(txId, index));
|
||||
}
|
||||
|
||||
public boolean isCompensationRequestPeriodValid(int blockHeight) {
|
||||
return bsqBlockChain.isCompensationRequestPeriodValid(blockHeight);
|
||||
}
|
||||
|
||||
public long getCreateCompensationRequestFee(int blockHeight) {
|
||||
return bsqBlockChain.getCreateCompensationRequestFee(blockHeight);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Block
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int getChainHeadHeight() {
|
||||
return bsqBlockChain.getChainHeadHeight();
|
||||
}
|
||||
|
||||
public boolean containsBlock(BsqBlock bsqBlock) {
|
||||
return bsqBlockChain.containsBlock(bsqBlock);
|
||||
}
|
||||
|
||||
public List<BsqBlock> getResetBlocksFrom(int fromBlockHeight) {
|
||||
return bsqBlockChain.getClonedBlocksFrom(fromBlockHeight);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Tx
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Map<String, Tx> getTxMap() {
|
||||
return bsqBlockChain.getTxMap();
|
||||
}
|
||||
|
||||
public Tx getGenesisTx() {
|
||||
return bsqBlockChain.getGenesisTx();
|
||||
}
|
||||
|
||||
public String getGenesisTxId() {
|
||||
return bsqBlockChain.getGenesisTxId();
|
||||
}
|
||||
|
@ -67,31 +78,48 @@ public class ReadModel {
|
|||
return bsqBlockChain.getGenesisBlockHeight();
|
||||
}
|
||||
|
||||
public boolean containsBlock(BsqBlock bsqBlock) {
|
||||
return bsqBlockChain.containsBlock(bsqBlock);
|
||||
public boolean containsTx(String txId) {
|
||||
return bsqBlockChain.containsTx(txId);
|
||||
}
|
||||
|
||||
public List<BsqBlock> getResetBlocksFrom(int fromBlockHeight) {
|
||||
return bsqBlockChain.getResetBlocksFrom(fromBlockHeight);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TxOutput
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Optional<TxOutput> getSpendableTxOutput(TxIdIndexTuple txIdIndexTuple) {
|
||||
return bsqBlockChain.getSpendableTxOutput(txIdIndexTuple);
|
||||
}
|
||||
|
||||
public Map<String, Tx> getTxMap() {
|
||||
return bsqBlockChain.getTxMap();
|
||||
public Optional<TxOutput> getSpendableTxOutput(String txId, int index) {
|
||||
return getSpendableTxOutput(new TxIdIndexTuple(txId, index));
|
||||
}
|
||||
|
||||
public Tx getGenesisTx() {
|
||||
return bsqBlockChain.getGenesisTx();
|
||||
public boolean isTxOutputSpendable(String txId, int index) {
|
||||
return bsqBlockChain.isTxOutputSpendable(txId, index);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Misc
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public boolean isCompensationRequestPeriodValid(int blockHeight) {
|
||||
return bsqBlockChain.isCompensationRequestPeriodValid(blockHeight);
|
||||
}
|
||||
|
||||
public long getCreateCompensationRequestFee(int blockHeight) {
|
||||
return bsqBlockChain.getCreateCompensationRequestFee(blockHeight);
|
||||
}
|
||||
|
||||
public Coin getIssuedAmount() {
|
||||
return bsqBlockChain.getIssuedAmount();
|
||||
}
|
||||
|
||||
public boolean containsTx(String txId) {
|
||||
return bsqBlockChain.containsTx(txId);
|
||||
public LinkedList<BsqBlock> getBsqBlocks() {
|
||||
return bsqBlockChain.getBsqBlocks();
|
||||
}
|
||||
|
||||
public boolean isTxOutputSpendable(String txId, int index) {
|
||||
return bsqBlockChain.isTxOutputSpendable(txId, index);
|
||||
public BsqBlockChain getClone() {
|
||||
return bsqBlockChain.getClone();
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package io.bisq.core.dao.blockchain;
|
||||
|
||||
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutput;
|
||||
|
@ -27,33 +26,53 @@ import javax.inject.Inject;
|
|||
/**
|
||||
* Encapsulates write access to BsqBlockChain.
|
||||
*/
|
||||
public class WriteModel {
|
||||
public class BsqBlockChainWriteModel {
|
||||
private BsqBlockChain bsqBlockChain;
|
||||
|
||||
@Inject
|
||||
public WriteModel(BsqBlockChain bsqBlockChain) {
|
||||
public BsqBlockChainWriteModel(BsqBlockChain bsqBlockChain) {
|
||||
this.bsqBlockChain = bsqBlockChain;
|
||||
}
|
||||
|
||||
public void removeUnspentTxOutput(TxOutput spendableTxOutput) {
|
||||
bsqBlockChain.removeUnspentTxOutput(spendableTxOutput);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Block
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addBlock(BsqBlock bsqBlock) {
|
||||
bsqBlockChain.addBlock(bsqBlock);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Tx
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setGenesisTx(Tx tx) {
|
||||
bsqBlockChain.setGenesisTx(tx);
|
||||
}
|
||||
|
||||
public void addTxToMap(Tx tx) {
|
||||
bsqBlockChain.addTxToMap(tx);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TxOutput
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addUnspentTxOutput(TxOutput txOutput) {
|
||||
bsqBlockChain.addUnspentTxOutput(txOutput);
|
||||
}
|
||||
|
||||
public void setGenesisTx(Tx tx) {
|
||||
bsqBlockChain.setGenesisTx(tx);
|
||||
public void removeUnspentTxOutput(TxOutput spendableTxOutput) {
|
||||
bsqBlockChain.removeUnspentTxOutput(spendableTxOutput);
|
||||
}
|
||||
|
||||
public void addBlock(BsqBlock bsqBlock) throws BlockNotConnectingException {
|
||||
bsqBlockChain.addBlock(bsqBlock);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Misc
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setCreateCompensationRequestFee(long value, int genesisBlockHeight) {
|
||||
bsqBlockChain.setCreateCompensationRequestFee(value, genesisBlockHeight);
|
|
@ -17,19 +17,81 @@
|
|||
|
||||
package io.bisq.core.dao.blockchain;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import io.bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import io.bisq.common.storage.Storage;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
//TODO move snapshot related code from bsqBlockChain here
|
||||
public class SnapshotManager {
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import java.io.File;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Manages snapshots of the BsqBlockChain.
|
||||
*/
|
||||
//TODO add tests; check if current logic is correct.
|
||||
@Slf4j
|
||||
public class SnapshotManager implements BsqBlockChain.Listener {
|
||||
private static final int SNAPSHOT_GRID = 100;
|
||||
|
||||
private final BsqBlockChain bsqBlockChain;
|
||||
private final Storage<BsqBlockChain> storage;
|
||||
private BsqBlockChain snapshotCandidate;
|
||||
|
||||
@Inject
|
||||
public SnapshotManager(BsqBlockChain bsqBlockChain) {
|
||||
public SnapshotManager(BsqBlockChain bsqBlockChain,
|
||||
PersistenceProtoResolver persistenceProtoResolver,
|
||||
@Named(Storage.STORAGE_DIR) File storageDir) {
|
||||
this.bsqBlockChain = bsqBlockChain;
|
||||
storage = new Storage<>(storageDir, persistenceProtoResolver);
|
||||
|
||||
bsqBlockChain.addListener(this);
|
||||
}
|
||||
|
||||
public void applySnapshot() {
|
||||
bsqBlockChain.applySnapshot();
|
||||
checkNotNull(storage, "storage must not be null");
|
||||
BsqBlockChain snapshot = storage.initAndGetPersistedWithFileName("BsqBlockChain", 100);
|
||||
if (snapshot != null) {
|
||||
log.info("applySnapshot snapshot.chainHeadHeight=" + snapshot.getChainHeadHeight());
|
||||
bsqBlockChain.applySnapshot(snapshot);
|
||||
} else {
|
||||
log.info("Try to apply snapshot but no stored snapshot available");
|
||||
}
|
||||
|
||||
bsqBlockChain.printDetails();
|
||||
}
|
||||
|
||||
private int getSnapshotHeight(int genesisHeight, int height, int grid) {
|
||||
return Math.round(Math.max(genesisHeight + 3 * grid, height) / grid) * grid - grid;
|
||||
}
|
||||
|
||||
private boolean isSnapshotHeight(int height) {
|
||||
return isSnapshotHeight(bsqBlockChain.getGenesisBlockHeight(), height, SNAPSHOT_GRID);
|
||||
}
|
||||
|
||||
private boolean isSnapshotHeight(int genesisHeight, int height, int grid) {
|
||||
return height % grid == 0 && height >= getSnapshotHeight(genesisHeight, height, grid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockAdded(BsqBlock bsqBlock) {
|
||||
final int chainHeadHeight = bsqBlockChain.getChainHeadHeight();
|
||||
if (isSnapshotHeight(chainHeadHeight) &&
|
||||
(snapshotCandidate == null ||
|
||||
snapshotCandidate.getChainHeadHeight() != chainHeadHeight)) {
|
||||
// At trigger event we store the latest snapshotCandidate to disc
|
||||
if (snapshotCandidate != null) {
|
||||
// We clone because storage is in a threaded context
|
||||
final BsqBlockChain cloned = bsqBlockChain.getClone(snapshotCandidate);
|
||||
storage.queueUpForSave(cloned);
|
||||
log.info("Saved snapshotCandidate to Disc at height " + chainHeadHeight);
|
||||
}
|
||||
// Now we clone and keep it in memory for the next trigger
|
||||
snapshotCandidate = bsqBlockChain.getClone(bsqBlockChain);
|
||||
// don't access cloned anymore with methods as locks are transient!
|
||||
log.debug("Cloned new snapshotCandidate at height " + chainHeadHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import io.bisq.common.storage.Storage;
|
|||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.core.dao.DaoOptionKeys;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChain;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutput;
|
||||
import io.bisq.core.dao.blockchain.vo.TxType;
|
||||
|
@ -45,18 +46,18 @@ import java.util.stream.Collectors;
|
|||
|
||||
@Slf4j
|
||||
public class JsonBlockChainExporter {
|
||||
private final BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
private final boolean dumpBlockchainData;
|
||||
private final BsqBlockChain bsqBlockChain;
|
||||
|
||||
private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", 1, 1, 1200);
|
||||
private File txDir, txOutputDir, bsqBlockChainDir;
|
||||
private JsonFileManager txFileManager, txOutputFileManager, bsqBlockChainFileManager;
|
||||
|
||||
@Inject
|
||||
public JsonBlockChainExporter(BsqBlockChain bsqBlockChain,
|
||||
public JsonBlockChainExporter(BsqBlockChainReadModel bsqBlockChainReadModel,
|
||||
@Named(Storage.STORAGE_DIR) File storageDir,
|
||||
@Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
|
||||
this.bsqBlockChain = bsqBlockChain;
|
||||
this.bsqBlockChainReadModel = bsqBlockChainReadModel;
|
||||
this.dumpBlockchainData = dumpBlockchainData;
|
||||
|
||||
init(storageDir, dumpBlockchainData);
|
||||
|
@ -104,7 +105,7 @@ public class JsonBlockChainExporter {
|
|||
public void maybeExport() {
|
||||
if (dumpBlockchainData) {
|
||||
ListenableFuture<Void> future = executor.submit(() -> {
|
||||
final BsqBlockChain bsqBlockChainClone = bsqBlockChain.getClone();
|
||||
final BsqBlockChain bsqBlockChainClone = bsqBlockChainReadModel.getClone();
|
||||
for (Tx tx : bsqBlockChainClone.getTxMap().values()) {
|
||||
String txId = tx.getId();
|
||||
JsonTxType txType = tx.getTxType() != TxType.UNDEFINED_TX_TYPE ? JsonTxType.valueOf(tx.getTxType().name()) : null;
|
||||
|
|
|
@ -20,9 +20,9 @@ package io.bisq.core.dao.node;
|
|||
import com.google.inject.Inject;
|
||||
import io.bisq.common.handlers.ErrorMessageHandler;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainListener;
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainWriteModel;
|
||||
import io.bisq.core.dao.blockchain.SnapshotManager;
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.provider.fee.FeeService;
|
||||
import io.bisq.network.p2p.P2PService;
|
||||
import io.bisq.network.p2p.P2PServiceListener;
|
||||
|
@ -42,13 +42,12 @@ public abstract class BsqNode {
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected final P2PService p2PService;
|
||||
protected final ReadModel readModel;
|
||||
protected final BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected final List<BsqBlockChainListener> bsqBlockChainListeners = new ArrayList<>();
|
||||
private final String genesisTxId;
|
||||
private final int genesisBlockHeight;
|
||||
private final SnapshotManager snapshotManager;
|
||||
@org.jetbrains.annotations.NotNull
|
||||
@Getter
|
||||
protected boolean parseBlockchainComplete;
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
|
@ -60,22 +59,22 @@ public abstract class BsqNode {
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@Inject
|
||||
public BsqNode(WriteModel writeModel,
|
||||
ReadModel readModel,
|
||||
public BsqNode(BsqBlockChainWriteModel bsqBlockChainWriteModel,
|
||||
BsqBlockChainReadModel bsqBlockChainReadModel,
|
||||
SnapshotManager snapshotManager,
|
||||
P2PService p2PService,
|
||||
FeeService feeService) {
|
||||
|
||||
this.p2PService = p2PService;
|
||||
this.readModel = readModel;
|
||||
this.bsqBlockChainReadModel = bsqBlockChainReadModel;
|
||||
|
||||
genesisTxId = readModel.getGenesisTxId();
|
||||
genesisBlockHeight = readModel.getGenesisBlockHeight();
|
||||
genesisTxId = bsqBlockChainReadModel.getGenesisTxId();
|
||||
genesisBlockHeight = bsqBlockChainReadModel.getGenesisBlockHeight();
|
||||
this.snapshotManager = snapshotManager;
|
||||
|
||||
writeModel.setCreateCompensationRequestFee(feeService.getCreateCompensationRequestFee().value,
|
||||
bsqBlockChainWriteModel.setCreateCompensationRequestFee(feeService.getCreateCompensationRequestFee().value,
|
||||
genesisBlockHeight);
|
||||
writeModel.setVotingFee(feeService.getVotingTxFee().value,
|
||||
bsqBlockChainWriteModel.setVotingFee(feeService.getVotingTxFee().value,
|
||||
genesisBlockHeight);
|
||||
}
|
||||
|
||||
|
@ -156,7 +155,7 @@ public abstract class BsqNode {
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected int getStartBlockHeight() {
|
||||
final int startBlockHeight = Math.max(genesisBlockHeight, readModel.getChainHeadHeight() + 1);
|
||||
final int startBlockHeight = Math.max(genesisBlockHeight, bsqBlockChainReadModel.getChainHeadHeight() + 1);
|
||||
log.info("Start parse blocks:\n" +
|
||||
" Start block height={}\n" +
|
||||
" Genesis txId={}\n" +
|
||||
|
@ -165,7 +164,7 @@ public abstract class BsqNode {
|
|||
startBlockHeight,
|
||||
genesisTxId,
|
||||
genesisBlockHeight,
|
||||
readModel.getChainHeadHeight());
|
||||
bsqBlockChainReadModel.getChainHeadHeight());
|
||||
|
||||
return startBlockHeight;
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
package io.bisq.core.dao.node;
|
||||
|
||||
import io.bisq.common.app.DevEnv;
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxInput;
|
||||
import io.bisq.core.dao.node.consensus.BsqTxVerification;
|
||||
import io.bisq.core.dao.node.consensus.GenesisTxVerification;
|
||||
import io.bisq.core.dao.node.consensus.BsqBlockController;
|
||||
import io.bisq.core.dao.node.consensus.BsqTxController;
|
||||
import io.bisq.core.dao.node.consensus.GenesisTxController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
@ -44,9 +44,9 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
@Slf4j
|
||||
@Immutable
|
||||
public abstract class BsqParser {
|
||||
protected final WriteModel writeModel;
|
||||
private final GenesisTxVerification genesisTxVerification;
|
||||
private final BsqTxVerification bsqTxVerification;
|
||||
protected final BsqBlockController bsqBlockController;
|
||||
private final GenesisTxController genesisTxController;
|
||||
private final BsqTxController bsqTxController;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -55,12 +55,12 @@ public abstract class BsqParser {
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@Inject
|
||||
public BsqParser(WriteModel writeModel,
|
||||
GenesisTxVerification genesisTxVerification,
|
||||
BsqTxVerification bsqTxVerification) {
|
||||
this.writeModel = writeModel;
|
||||
this.genesisTxVerification = genesisTxVerification;
|
||||
this.bsqTxVerification = bsqTxVerification;
|
||||
public BsqParser(BsqBlockController bsqBlockController,
|
||||
GenesisTxController genesisTxController,
|
||||
BsqTxController bsqTxController) {
|
||||
this.bsqBlockController = bsqBlockController;
|
||||
this.genesisTxController = genesisTxController;
|
||||
this.bsqTxController = bsqTxController;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -70,8 +70,8 @@ public abstract class BsqParser {
|
|||
protected void checkForGenesisTx(int blockHeight,
|
||||
List<Tx> bsqTxsInBlock,
|
||||
Tx tx) {
|
||||
if (genesisTxVerification.isGenesisTx(tx, blockHeight)) {
|
||||
genesisTxVerification.applyStateChange(tx);
|
||||
if (genesisTxController.isGenesisTx(tx, blockHeight)) {
|
||||
genesisTxController.applyStateChange(tx);
|
||||
bsqTxsInBlock.add(tx);
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ public abstract class BsqParser {
|
|||
|
||||
// we check if we have any valid BSQ from that tx set
|
||||
bsqTxsInBlock.addAll(txsWithoutInputsFromSameBlock.stream()
|
||||
.filter(tx -> bsqTxVerification.isBsqTx(blockHeight, tx))
|
||||
.filter(tx -> bsqTxController.isBsqTx(blockHeight, tx))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
log.debug("Parsing of all txsWithoutInputsFromSameBlock is done.");
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.node.consensus;
|
||||
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainWriteModel;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.LinkedList;
|
||||
|
||||
@Slf4j
|
||||
public class BsqBlockController {
|
||||
|
||||
private final BsqBlockChainWriteModel bsqBlockChainWriteModel;
|
||||
private final BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
|
||||
@Inject
|
||||
public BsqBlockController(BsqBlockChainWriteModel bsqBlockChainWriteModel, BsqBlockChainReadModel bsqBlockChainReadModel) {
|
||||
this.bsqBlockChainWriteModel = bsqBlockChainWriteModel;
|
||||
this.bsqBlockChainReadModel = bsqBlockChainReadModel;
|
||||
}
|
||||
|
||||
public void addBlockIfValid(BsqBlock bsqBlock) throws BlockNotConnectingException {
|
||||
LinkedList<BsqBlock> bsqBlocks = bsqBlockChainReadModel.getBsqBlocks();
|
||||
if (!bsqBlocks.contains(bsqBlock)) {
|
||||
if (isBlockConnecting(bsqBlock, bsqBlocks)) {
|
||||
bsqBlockChainWriteModel.addBlock(bsqBlock);
|
||||
} else {
|
||||
log.warn("addBlock called with a not connecting block:\n" +
|
||||
"height()={}, hash()={}, head.height()={}, head.hash()={}",
|
||||
bsqBlock.getHeight(), bsqBlock.getHash(), bsqBlocks.getLast().getHeight(), bsqBlocks.getLast().getHash());
|
||||
throw new BlockNotConnectingException(bsqBlock);
|
||||
}
|
||||
} else {
|
||||
log.warn("We got that block already. Ignore the call.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBlockConnecting(BsqBlock bsqBlock, LinkedList<BsqBlock> bsqBlocks) {
|
||||
// Case 1: bsqBlocks is empty
|
||||
// Case 2: bsqBlocks not empty. Last block must match new blocks getPreviousBlockHash and
|
||||
// height of last block +1 must be new blocks height
|
||||
return bsqBlocks.isEmpty() ||
|
||||
(bsqBlocks.getLast().getHash().equals(bsqBlock.getPreviousBlockHash()) &&
|
||||
bsqBlocks.getLast().getHeight() + 1 == bsqBlock.getHeight());
|
||||
}
|
||||
}
|
|
@ -29,25 +29,25 @@ import javax.inject.Inject;
|
|||
* Verifies if a given transaction is a BSQ transaction.
|
||||
*/
|
||||
@Slf4j
|
||||
public class BsqTxVerification {
|
||||
public class BsqTxController {
|
||||
|
||||
private final TxInputsVerification txInputsVerification;
|
||||
private final TxOutputsVerification txOutputsVerification;
|
||||
private final TxInputsController txInputsController;
|
||||
private final TxOutputsController txOutputsController;
|
||||
|
||||
@Inject
|
||||
public BsqTxVerification(TxInputsVerification txInputsVerification,
|
||||
TxOutputsVerification txOutputsVerification) {
|
||||
this.txInputsVerification = txInputsVerification;
|
||||
this.txOutputsVerification = txOutputsVerification;
|
||||
public BsqTxController(TxInputsController txInputsController,
|
||||
TxOutputsController txOutputsController) {
|
||||
this.txInputsController = txInputsController;
|
||||
this.txOutputsController = txOutputsController;
|
||||
}
|
||||
|
||||
public boolean isBsqTx(int blockHeight, Tx tx) {
|
||||
BsqInputBalance bsqInputBalance = txInputsVerification.getBsqInputBalance(tx, blockHeight);
|
||||
BsqInputBalance bsqInputBalance = txInputsController.getBsqInputBalance(tx, blockHeight);
|
||||
|
||||
final boolean bsqInputBalancePositive = bsqInputBalance.isPositive();
|
||||
if (bsqInputBalancePositive) {
|
||||
txInputsVerification.applyStateChange(tx);
|
||||
txOutputsVerification.iterate(tx, blockHeight, bsqInputBalance);
|
||||
txInputsController.applyStateChange(tx);
|
||||
txOutputsController.iterate(tx, blockHeight, bsqInputBalance);
|
||||
}
|
||||
|
||||
// Lets check if we have left over BSQ (burned fees)
|
|
@ -18,7 +18,7 @@
|
|||
package io.bisq.core.dao.node.consensus;
|
||||
|
||||
import io.bisq.common.app.Version;
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutput;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutputType;
|
||||
|
@ -31,23 +31,23 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
/**
|
||||
* Verifies if OP_RETURN data matches rules for a compensation request tx and applies state change.
|
||||
*/
|
||||
public class CompensationRequestVerification {
|
||||
private final ReadModel readModel;
|
||||
public class CompensationRequestController {
|
||||
private final BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
|
||||
@Inject
|
||||
public CompensationRequestVerification(ReadModel readModel) {
|
||||
this.readModel = readModel;
|
||||
public CompensationRequestController(BsqBlockChainReadModel bsqBlockChainReadModel) {
|
||||
this.bsqBlockChainReadModel = bsqBlockChainReadModel;
|
||||
}
|
||||
|
||||
public boolean verify(byte[] opReturnData, long bsqFee, int blockHeight, TxOutputsVerification.MutableState mutableState) {
|
||||
public boolean verify(byte[] opReturnData, long bsqFee, int blockHeight, TxOutputsController.MutableState mutableState) {
|
||||
return mutableState.getCompRequestIssuanceOutputCandidate() != null &&
|
||||
opReturnData.length == 22 &&
|
||||
Version.COMPENSATION_REQUEST_VERSION == opReturnData[1] &&
|
||||
bsqFee == readModel.getCreateCompensationRequestFee(blockHeight) &&
|
||||
readModel.isCompensationRequestPeriodValid(blockHeight);
|
||||
bsqFee == bsqBlockChainReadModel.getCreateCompensationRequestFee(blockHeight) &&
|
||||
bsqBlockChainReadModel.isCompensationRequestPeriodValid(blockHeight);
|
||||
}
|
||||
|
||||
public void applyStateChange(Tx tx, TxOutput opReturnTxOutput, TxOutputsVerification.MutableState mutableState) {
|
||||
public void applyStateChange(Tx tx, TxOutput opReturnTxOutput, TxOutputsController.MutableState mutableState) {
|
||||
opReturnTxOutput.setTxOutputType(TxOutputType.COMPENSATION_REQUEST_OP_RETURN_OUTPUT);
|
||||
checkArgument(mutableState.getCompRequestIssuanceOutputCandidate() != null, "mutableState.getCompRequestIssuanceOutputCandidate() must nto be null");
|
||||
mutableState.getCompRequestIssuanceOutputCandidate().setTxOutputType(TxOutputType.COMPENSATION_REQUEST_ISSUANCE_CANDIDATE_OUTPUT);
|
|
@ -18,7 +18,7 @@
|
|||
package io.bisq.core.dao.node.consensus;
|
||||
|
||||
import io.bisq.core.dao.DaoOptionKeys;
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainWriteModel;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxType;
|
||||
|
||||
|
@ -28,17 +28,17 @@ import javax.inject.Named;
|
|||
/**
|
||||
* Verifies if a given transaction is a BSQ genesis transaction.
|
||||
*/
|
||||
public class GenesisTxVerification {
|
||||
public class GenesisTxController {
|
||||
|
||||
private final WriteModel writeModel;
|
||||
private final BsqBlockChainWriteModel bsqBlockChainWriteModel;
|
||||
private final String genesisTxId;
|
||||
private final int genesisBlockHeight;
|
||||
|
||||
@Inject
|
||||
public GenesisTxVerification(WriteModel writeModel,
|
||||
@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId,
|
||||
@Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) int genesisBlockHeight) {
|
||||
this.writeModel = writeModel;
|
||||
public GenesisTxController(BsqBlockChainWriteModel bsqBlockChainWriteModel,
|
||||
@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId,
|
||||
@Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) int genesisBlockHeight) {
|
||||
this.bsqBlockChainWriteModel = bsqBlockChainWriteModel;
|
||||
this.genesisTxId = genesisTxId;
|
||||
this.genesisBlockHeight = genesisBlockHeight;
|
||||
}
|
||||
|
@ -51,11 +51,11 @@ public class GenesisTxVerification {
|
|||
tx.getOutputs().forEach(txOutput -> {
|
||||
txOutput.setUnspent(true);
|
||||
txOutput.setVerified(true);
|
||||
writeModel.addUnspentTxOutput(txOutput);
|
||||
bsqBlockChainWriteModel.addUnspentTxOutput(txOutput);
|
||||
});
|
||||
tx.setTxType(TxType.GENESIS);
|
||||
|
||||
writeModel.setGenesisTx(tx);
|
||||
writeModel.addTxToMap(tx);
|
||||
bsqBlockChainWriteModel.setGenesisTx(tx);
|
||||
bsqBlockChainWriteModel.addTxToMap(tx);
|
||||
}
|
||||
}
|
|
@ -22,12 +22,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import javax.inject.Inject;
|
||||
|
||||
@Slf4j
|
||||
public class IssuanceVerification {
|
||||
public class IssuanceController {
|
||||
/* private static final long MIN_BSQ_ISSUANCE_AMOUNT = 1000;
|
||||
private static final long MAX_BSQ_ISSUANCE_AMOUNT = 10_000_000;*/
|
||||
|
||||
@Inject
|
||||
public IssuanceVerification() {
|
||||
public IssuanceController() {
|
||||
}
|
||||
|
||||
/* public boolean maybeProcessData(Tx tx) {
|
|
@ -31,18 +31,18 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
* Verifies if a given transaction is a BSQ OP_RETURN transaction.
|
||||
*/
|
||||
@Slf4j
|
||||
public class OpReturnVerification {
|
||||
private final CompensationRequestVerification compensationRequestVerification;
|
||||
private final VotingVerification votingVerification;
|
||||
public class OpReturnController {
|
||||
private final CompensationRequestController compensationRequestController;
|
||||
private final VotingController votingController;
|
||||
|
||||
@Inject
|
||||
public OpReturnVerification(CompensationRequestVerification compensationRequestVerification,
|
||||
VotingVerification votingVerification) {
|
||||
this.compensationRequestVerification = compensationRequestVerification;
|
||||
this.votingVerification = votingVerification;
|
||||
public OpReturnController(CompensationRequestController compensationRequestController,
|
||||
VotingController votingController) {
|
||||
this.compensationRequestController = compensationRequestController;
|
||||
this.votingController = votingController;
|
||||
}
|
||||
|
||||
public void process(TxOutput txOutput, Tx tx, int index, long bsqFee, int blockHeight, TxOutputsVerification.MutableState mutableState) {
|
||||
public void process(TxOutput txOutput, Tx tx, int index, long bsqFee, int blockHeight, TxOutputsController.MutableState mutableState) {
|
||||
final long txOutputValue = txOutput.getValue();
|
||||
// A BSQ OP_RETURN has to be the last output, the txOutputValue has to be 0 as well as there have to be a BSQ fee.
|
||||
if (txOutputValue == 0 && index == tx.getOutputs().size() - 1 && bsqFee > 0) {
|
||||
|
@ -53,8 +53,8 @@ public class OpReturnVerification {
|
|||
// Check with the type byte which kind of OP_RETURN we have.
|
||||
switch (opReturnData[0]) {
|
||||
case OpReturnTypes.COMPENSATION_REQUEST:
|
||||
if (compensationRequestVerification.verify(opReturnData, bsqFee, blockHeight, mutableState)) {
|
||||
compensationRequestVerification.applyStateChange(tx, txOutput, mutableState);
|
||||
if (compensationRequestController.verify(opReturnData, bsqFee, blockHeight, mutableState)) {
|
||||
compensationRequestController.applyStateChange(tx, txOutput, mutableState);
|
||||
}
|
||||
case OpReturnTypes.VOTE:
|
||||
// TODO
|
|
@ -19,9 +19,9 @@ package io.bisq.core.dao.node.consensus;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class PeriodVerification {
|
||||
public class PeriodController {
|
||||
|
||||
@Inject
|
||||
public PeriodVerification() {
|
||||
public PeriodController() {
|
||||
}
|
||||
}
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
package io.bisq.core.dao.node.consensus;
|
||||
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainWriteModel;
|
||||
import io.bisq.core.dao.blockchain.vo.SpentInfo;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxInput;
|
||||
|
@ -33,27 +33,27 @@ import java.util.Optional;
|
|||
*/
|
||||
|
||||
@Slf4j
|
||||
public class TxInputVerification {
|
||||
public class TxInputController {
|
||||
|
||||
private final WriteModel writeModel;
|
||||
private final ReadModel readModel;
|
||||
private final BsqBlockChainWriteModel bsqBlockChainWriteModel;
|
||||
private final BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
|
||||
@Inject
|
||||
public TxInputVerification(WriteModel writeModel, ReadModel readModel) {
|
||||
this.writeModel = writeModel;
|
||||
this.readModel = readModel;
|
||||
public TxInputController(BsqBlockChainWriteModel bsqBlockChainWriteModel, BsqBlockChainReadModel bsqBlockChainReadModel) {
|
||||
this.bsqBlockChainWriteModel = bsqBlockChainWriteModel;
|
||||
this.bsqBlockChainReadModel = bsqBlockChainReadModel;
|
||||
}
|
||||
|
||||
Optional<TxOutput> getOptionalSpendableTxOutput(TxInput input) {
|
||||
// TODO check if Tuple indexes of inputs outputs are not messed up...
|
||||
// Get spendable BSQ output for txIdIndexTuple... (get output used as input in tx if it's spendable BSQ)
|
||||
return readModel.getSpendableTxOutput(input.getTxIdIndexTuple());
|
||||
return bsqBlockChainReadModel.getSpendableTxOutput(input.getTxIdIndexTuple());
|
||||
}
|
||||
|
||||
void applyStateChange(TxInput input, TxOutput spendableTxOutput, int blockHeight, Tx tx, int inputIndex) {
|
||||
// The output is BSQ, set it as spent, update bsqBlockChain and add to available BSQ for this tx
|
||||
spendableTxOutput.setUnspent(false);
|
||||
writeModel.removeUnspentTxOutput(spendableTxOutput);
|
||||
bsqBlockChainWriteModel.removeUnspentTxOutput(spendableTxOutput);
|
||||
spendableTxOutput.setSpentInfo(new SpentInfo(blockHeight, tx.getId(), inputIndex));
|
||||
input.setConnectedTxOutput(spendableTxOutput);
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package io.bisq.core.dao.node.consensus;
|
||||
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainWriteModel;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxInput;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutput;
|
||||
|
@ -30,31 +30,31 @@ import java.util.Optional;
|
|||
* Calculate the available BSQ balance from all inputs and apply state change.
|
||||
*/
|
||||
@Slf4j
|
||||
public class TxInputsVerification {
|
||||
public class TxInputsController {
|
||||
|
||||
private final WriteModel writeModel;
|
||||
private final TxInputVerification txInputVerification;
|
||||
private final BsqBlockChainWriteModel bsqBlockChainWriteModel;
|
||||
private final TxInputController txInputController;
|
||||
|
||||
@Inject
|
||||
public TxInputsVerification(WriteModel writeModel, TxInputVerification txInputVerification) {
|
||||
this.writeModel = writeModel;
|
||||
this.txInputVerification = txInputVerification;
|
||||
public TxInputsController(BsqBlockChainWriteModel bsqBlockChainWriteModel, TxInputController txInputController) {
|
||||
this.bsqBlockChainWriteModel = bsqBlockChainWriteModel;
|
||||
this.txInputController = txInputController;
|
||||
}
|
||||
|
||||
BsqTxVerification.BsqInputBalance getBsqInputBalance(Tx tx, int blockHeight) {
|
||||
BsqTxVerification.BsqInputBalance bsqInputBalance = new BsqTxVerification.BsqInputBalance();
|
||||
BsqTxController.BsqInputBalance getBsqInputBalance(Tx tx, int blockHeight) {
|
||||
BsqTxController.BsqInputBalance bsqInputBalance = new BsqTxController.BsqInputBalance();
|
||||
for (int inputIndex = 0; inputIndex < tx.getInputs().size(); inputIndex++) {
|
||||
TxInput input = tx.getInputs().get(inputIndex);
|
||||
final Optional<TxOutput> optionalSpendableTxOutput = txInputVerification.getOptionalSpendableTxOutput(input);
|
||||
final Optional<TxOutput> optionalSpendableTxOutput = txInputController.getOptionalSpendableTxOutput(input);
|
||||
if (optionalSpendableTxOutput.isPresent()) {
|
||||
bsqInputBalance.add(optionalSpendableTxOutput.get().getValue());
|
||||
txInputVerification.applyStateChange(input, optionalSpendableTxOutput.get(), blockHeight, tx, inputIndex);
|
||||
txInputController.applyStateChange(input, optionalSpendableTxOutput.get(), blockHeight, tx, inputIndex);
|
||||
}
|
||||
}
|
||||
return bsqInputBalance;
|
||||
}
|
||||
|
||||
void applyStateChange(Tx tx) {
|
||||
writeModel.addTxToMap(tx);
|
||||
bsqBlockChainWriteModel.addTxToMap(tx);
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package io.bisq.core.dao.node.consensus;
|
||||
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainWriteModel;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutput;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutputType;
|
||||
|
@ -33,22 +33,22 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
*/
|
||||
|
||||
@Slf4j
|
||||
public class TxOutputVerification {
|
||||
private final WriteModel writeModel;
|
||||
private final OpReturnVerification opReturnVerification;
|
||||
public class TxOutputController {
|
||||
private final BsqBlockChainWriteModel bsqBlockChainWriteModel;
|
||||
private final OpReturnController opReturnController;
|
||||
|
||||
@Inject
|
||||
public TxOutputVerification(WriteModel writeModel, OpReturnVerification opReturnVerification) {
|
||||
this.writeModel = writeModel;
|
||||
this.opReturnVerification = opReturnVerification;
|
||||
public TxOutputController(BsqBlockChainWriteModel bsqBlockChainWriteModel, OpReturnController opReturnController) {
|
||||
this.bsqBlockChainWriteModel = bsqBlockChainWriteModel;
|
||||
this.opReturnController = opReturnController;
|
||||
}
|
||||
|
||||
void verify(Tx tx,
|
||||
TxOutput txOutput,
|
||||
int index,
|
||||
int blockHeight,
|
||||
BsqTxVerification.BsqInputBalance bsqInputBalance,
|
||||
TxOutputsVerification.MutableState mutableState) {
|
||||
BsqTxController.BsqInputBalance bsqInputBalance,
|
||||
TxOutputsController.MutableState mutableState) {
|
||||
|
||||
final long txOutputValue = txOutput.getValue();
|
||||
final long bsqInputBalanceValue = bsqInputBalance.getValue();
|
||||
|
@ -86,7 +86,7 @@ public class TxOutputVerification {
|
|||
}
|
||||
} else {
|
||||
// We got a OP_RETURN output.
|
||||
opReturnVerification.process(txOutput, tx, index, bsqInputBalanceValue, blockHeight, mutableState);
|
||||
opReturnController.process(txOutput, tx, index, bsqInputBalanceValue, blockHeight, mutableState);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -99,7 +99,7 @@ public class TxOutputVerification {
|
|||
txOutput.setVerified(true);
|
||||
txOutput.setUnspent(true);
|
||||
txOutput.setTxOutputType(TxOutputType.BSQ_OUTPUT);
|
||||
writeModel.addUnspentTxOutput(txOutput);
|
||||
bsqBlockChainWriteModel.addUnspentTxOutput(txOutput);
|
||||
}
|
||||
|
||||
private void applyStateChangeForBtcOutput(TxOutput txOutput) {
|
|
@ -32,21 +32,21 @@ import java.util.List;
|
|||
*/
|
||||
|
||||
@Slf4j
|
||||
public class TxOutputsVerification {
|
||||
public class TxOutputsController {
|
||||
|
||||
private final TxOutputVerification txOutputVerification;
|
||||
private final TxOutputController txOutputController;
|
||||
|
||||
@Inject
|
||||
public TxOutputsVerification(TxOutputVerification txOutputVerification) {
|
||||
this.txOutputVerification = txOutputVerification;
|
||||
public TxOutputsController(TxOutputController txOutputController) {
|
||||
this.txOutputController = txOutputController;
|
||||
}
|
||||
|
||||
void iterate(Tx tx, int blockHeight, BsqTxVerification.BsqInputBalance bsqInputBalance) {
|
||||
void iterate(Tx tx, int blockHeight, BsqTxController.BsqInputBalance bsqInputBalance) {
|
||||
// We use order of output index. An output is a BSQ utxo as long there is enough input value
|
||||
final List<TxOutput> outputs = tx.getOutputs();
|
||||
MutableState mutableState = new MutableState();
|
||||
for (int index = 0; index < outputs.size(); index++) {
|
||||
txOutputVerification.verify(tx, outputs.get(index), index, blockHeight, bsqInputBalance, mutableState);
|
||||
txOutputController.verify(tx, outputs.get(index), index, blockHeight, bsqInputBalance, mutableState);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,9 +22,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import javax.inject.Inject;
|
||||
|
||||
@Slf4j
|
||||
public class VotingVerification {
|
||||
public class VotingController {
|
||||
|
||||
@Inject
|
||||
public VotingVerification() {
|
||||
public VotingController() {
|
||||
}
|
||||
}
|
|
@ -21,9 +21,9 @@ import com.google.inject.Inject;
|
|||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.handlers.ErrorMessageHandler;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainListener;
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainWriteModel;
|
||||
import io.bisq.core.dao.blockchain.SnapshotManager;
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
|
||||
import io.bisq.core.dao.blockchain.json.JsonBlockChainExporter;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
|
@ -51,16 +51,16 @@ public class FullNode extends BsqNode {
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@Inject
|
||||
public FullNode(WriteModel writeModel,
|
||||
ReadModel readModel,
|
||||
public FullNode(BsqBlockChainWriteModel bsqBlockChainWriteModel,
|
||||
BsqBlockChainReadModel bsqBlockChainReadModel,
|
||||
SnapshotManager snapshotManager,
|
||||
P2PService p2PService,
|
||||
FullNodeExecutor bsqFullNodeExecutor,
|
||||
JsonBlockChainExporter jsonBlockChainExporter,
|
||||
FeeService feeService,
|
||||
FullNodeNetworkManager fullNodeNetworkManager) {
|
||||
super(writeModel,
|
||||
readModel,
|
||||
super(bsqBlockChainWriteModel,
|
||||
bsqBlockChainReadModel,
|
||||
snapshotManager,
|
||||
p2PService,
|
||||
feeService);
|
||||
|
|
|
@ -20,14 +20,14 @@ package io.bisq.core.dao.node.full;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.neemre.btcdcli4j.core.domain.Block;
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BsqBlockchainException;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.node.BsqParser;
|
||||
import io.bisq.core.dao.node.consensus.BsqTxVerification;
|
||||
import io.bisq.core.dao.node.consensus.GenesisTxVerification;
|
||||
import io.bisq.core.dao.node.consensus.BsqBlockController;
|
||||
import io.bisq.core.dao.node.consensus.BsqTxController;
|
||||
import io.bisq.core.dao.node.consensus.GenesisTxController;
|
||||
import io.bisq.core.dao.node.full.rpc.RpcService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
@ -57,10 +57,10 @@ public class FullNodeParser extends BsqParser {
|
|||
|
||||
@Inject
|
||||
public FullNodeParser(RpcService rpcService,
|
||||
WriteModel writeModel,
|
||||
GenesisTxVerification genesisTxVerification,
|
||||
BsqTxVerification bsqTxVerification) {
|
||||
super(writeModel, genesisTxVerification, bsqTxVerification);
|
||||
BsqBlockController bsqBlockController,
|
||||
GenesisTxController genesisTxController,
|
||||
BsqTxController bsqTxController) {
|
||||
super(bsqBlockController, genesisTxController, bsqTxController);
|
||||
this.rpcService = rpcService;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ public class FullNodeParser extends BsqParser {
|
|||
btcdBlock.getHash(),
|
||||
btcdBlock.getPreviousBlockHash(),
|
||||
ImmutableList.copyOf(bsqTxsInBlock));
|
||||
writeModel.addBlock(bsqBlock);
|
||||
bsqBlockController.addBlockIfValid(bsqBlock);
|
||||
log.info("parseBlock took {} ms at blockHeight {}; bsqTxsInBlock.size={}",
|
||||
System.currentTimeMillis() - startTs, bsqBlock.getHeight(), bsqTxsInBlock.size());
|
||||
return bsqBlock;
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.bisq.core.dao.node.full.network;
|
|||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.app.Log;
|
||||
import io.bisq.common.proto.network.NetworkEnvelope;
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import io.bisq.core.dao.node.messages.GetBsqBlocksRequest;
|
||||
import io.bisq.core.dao.node.messages.NewBsqBlockBroadcastMessage;
|
||||
|
@ -35,7 +35,7 @@ public class FullNodeNetworkManager implements MessageListener, PeerManager.List
|
|||
private final NetworkNode networkNode;
|
||||
private final PeerManager peerManager;
|
||||
private final Broadcaster broadcaster;
|
||||
private final ReadModel readModel;
|
||||
private final BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
|
||||
// Key is connection UID
|
||||
private final Map<String, GetBsqBlocksRequestHandler> getBlocksRequestHandlers = new HashMap<>();
|
||||
|
@ -50,11 +50,11 @@ public class FullNodeNetworkManager implements MessageListener, PeerManager.List
|
|||
public FullNodeNetworkManager(NetworkNode networkNode,
|
||||
PeerManager peerManager,
|
||||
Broadcaster broadcaster,
|
||||
ReadModel readModel) {
|
||||
BsqBlockChainReadModel bsqBlockChainReadModel) {
|
||||
this.networkNode = networkNode;
|
||||
this.peerManager = peerManager;
|
||||
this.broadcaster = broadcaster;
|
||||
this.readModel = readModel;
|
||||
this.bsqBlockChainReadModel = bsqBlockChainReadModel;
|
||||
// seedNodeAddresses can be empty (in case there is only 1 seed node, the seed node starting up has no other seed nodes)
|
||||
|
||||
networkNode.addMessageListener(this);
|
||||
|
@ -113,7 +113,7 @@ public class FullNodeNetworkManager implements MessageListener, PeerManager.List
|
|||
final String uid = connection.getUid();
|
||||
if (!getBlocksRequestHandlers.containsKey(uid)) {
|
||||
GetBsqBlocksRequestHandler requestHandler = new GetBsqBlocksRequestHandler(networkNode,
|
||||
readModel,
|
||||
bsqBlockChainReadModel,
|
||||
new GetBsqBlocksRequestHandler.Listener() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import com.google.common.util.concurrent.SettableFuture;
|
|||
import io.bisq.common.Timer;
|
||||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.app.Log;
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import io.bisq.core.dao.node.messages.GetBsqBlocksRequest;
|
||||
import io.bisq.core.dao.node.messages.GetBsqBlocksResponse;
|
||||
|
@ -43,7 +43,7 @@ class GetBsqBlocksRequestHandler {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private final NetworkNode networkNode;
|
||||
private final ReadModel readModel;
|
||||
private final BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
private final Listener listener;
|
||||
private Timer timeoutTimer;
|
||||
private boolean stopped;
|
||||
|
@ -53,9 +53,9 @@ class GetBsqBlocksRequestHandler {
|
|||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public GetBsqBlocksRequestHandler(NetworkNode networkNode, ReadModel readModel, Listener listener) {
|
||||
public GetBsqBlocksRequestHandler(NetworkNode networkNode, BsqBlockChainReadModel bsqBlockChainReadModel, Listener listener) {
|
||||
this.networkNode = networkNode;
|
||||
this.readModel = readModel;
|
||||
this.bsqBlockChainReadModel = bsqBlockChainReadModel;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ class GetBsqBlocksRequestHandler {
|
|||
|
||||
public void onGetBsqBlocksRequest(GetBsqBlocksRequest getBsqBlocksRequest, final Connection connection) {
|
||||
Log.traceCall(getBsqBlocksRequest + "\n\tconnection=" + connection);
|
||||
List<BsqBlock> bsqBlocks = readModel.getResetBlocksFrom(getBsqBlocksRequest.getFromBlockHeight());
|
||||
List<BsqBlock> bsqBlocks = bsqBlockChainReadModel.getResetBlocksFrom(getBsqBlocksRequest.getFromBlockHeight());
|
||||
final GetBsqBlocksResponse bsqBlocksResponse = new GetBsqBlocksResponse(bsqBlocks, getBsqBlocksRequest.getNonce());
|
||||
log.debug("bsqBlocksResponse " + bsqBlocksResponse.getRequestNonce());
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ import com.google.inject.Inject;
|
|||
import io.bisq.common.UserThread;
|
||||
import io.bisq.common.handlers.ErrorMessageHandler;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainListener;
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainWriteModel;
|
||||
import io.bisq.core.dao.blockchain.SnapshotManager;
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import io.bisq.core.dao.node.BsqNode;
|
||||
|
@ -58,15 +58,15 @@ public class LiteNode extends BsqNode {
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@Inject
|
||||
public LiteNode(WriteModel writeModel,
|
||||
ReadModel readModel,
|
||||
public LiteNode(BsqBlockChainWriteModel bsqBlockChainWriteModel,
|
||||
BsqBlockChainReadModel bsqBlockChainReadModel,
|
||||
SnapshotManager snapshotManager,
|
||||
P2PService p2PService,
|
||||
LiteNodeExecutor bsqLiteNodeExecutor,
|
||||
FeeService feeService,
|
||||
LiteNodeNetworkManager liteNodeNetworkManager) {
|
||||
super(writeModel,
|
||||
readModel,
|
||||
super(bsqBlockChainWriteModel,
|
||||
bsqBlockChainReadModel,
|
||||
snapshotManager,
|
||||
p2PService,
|
||||
feeService);
|
||||
|
@ -150,7 +150,7 @@ public class LiteNode extends BsqNode {
|
|||
// We reset all mutable data in case the provider would not have done it.
|
||||
bsqBlock.reset();
|
||||
log.info("onNewBlockReceived: bsqBlock={}", bsqBlock.getHeight());
|
||||
if (!readModel.containsBlock(bsqBlock)) {
|
||||
if (!bsqBlockChainReadModel.containsBlock(bsqBlock)) {
|
||||
bsqLiteNodeExecutor.parseBlock(bsqBlock,
|
||||
this::notifyListenersOnNewBlock,
|
||||
getErrorHandler());
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
|
||||
package io.bisq.core.dao.node.lite;
|
||||
|
||||
import io.bisq.core.dao.blockchain.WriteModel;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
|
||||
import io.bisq.core.dao.blockchain.vo.BsqBlock;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.node.BsqParser;
|
||||
import io.bisq.core.dao.node.consensus.BsqTxVerification;
|
||||
import io.bisq.core.dao.node.consensus.GenesisTxVerification;
|
||||
import io.bisq.core.dao.node.consensus.BsqBlockController;
|
||||
import io.bisq.core.dao.node.consensus.BsqTxController;
|
||||
import io.bisq.core.dao.node.consensus.GenesisTxController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
@ -40,10 +40,10 @@ import java.util.function.Consumer;
|
|||
public class LiteNodeParser extends BsqParser {
|
||||
|
||||
@Inject
|
||||
public LiteNodeParser(WriteModel writeModel,
|
||||
GenesisTxVerification genesisTxVerification,
|
||||
BsqTxVerification bsqTxVerification) {
|
||||
super(writeModel, genesisTxVerification, bsqTxVerification);
|
||||
public LiteNodeParser(BsqBlockController bsqBlockController,
|
||||
GenesisTxController genesisTxController,
|
||||
BsqTxController bsqTxController) {
|
||||
super(bsqBlockController, genesisTxController, bsqTxController);
|
||||
}
|
||||
|
||||
void parseBsqBlocks(List<BsqBlock> bsqBlocks,
|
||||
|
@ -62,6 +62,6 @@ public class LiteNodeParser extends BsqParser {
|
|||
List<Tx> bsqTxsInBlock = new ArrayList<>();
|
||||
bsqBlock.getTxs().forEach(tx -> checkForGenesisTx(blockHeight, bsqTxsInBlock, tx));
|
||||
recursiveFindBsqTxs(bsqTxsInBlock, txList, blockHeight, 0, 5300);
|
||||
writeModel.addBlock(bsqBlock);
|
||||
bsqBlockController.addBlockIfValid(bsqBlock);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import io.bisq.core.btc.wallet.BtcWalletService;
|
|||
import io.bisq.core.dao.DaoPeriodService;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainChangeDispatcher;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainListener;
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.request.compensation.consensus.OpReturnData;
|
||||
import io.bisq.core.dao.request.compensation.consensus.Restrictions;
|
||||
import io.bisq.core.provider.fee.FeeService;
|
||||
|
@ -68,7 +68,7 @@ public class CompensationRequestManager implements PersistedDataHost, BsqBlockCh
|
|||
private final DaoPeriodService daoPeriodService;
|
||||
private final BsqWalletService bsqWalletService;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final ReadModel readModel;
|
||||
private final BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
private final Storage<CompensationRequestList> compensationRequestsStorage;
|
||||
private final PublicKey signaturePubKey;
|
||||
private final FeeService feeService;
|
||||
|
@ -90,7 +90,7 @@ public class CompensationRequestManager implements PersistedDataHost, BsqBlockCh
|
|||
BsqWalletService bsqWalletService,
|
||||
BtcWalletService btcWalletService,
|
||||
DaoPeriodService daoPeriodService,
|
||||
ReadModel readModel,
|
||||
BsqBlockChainReadModel bsqBlockChainReadModel,
|
||||
BsqBlockChainChangeDispatcher bsqBlockChainChangeDispatcher,
|
||||
KeyRing keyRing,
|
||||
Storage<CompensationRequestList> compensationRequestsStorage,
|
||||
|
@ -99,7 +99,7 @@ public class CompensationRequestManager implements PersistedDataHost, BsqBlockCh
|
|||
this.bsqWalletService = bsqWalletService;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.daoPeriodService = daoPeriodService;
|
||||
this.readModel = readModel;
|
||||
this.bsqBlockChainReadModel = bsqBlockChainReadModel;
|
||||
this.compensationRequestsStorage = compensationRequestsStorage;
|
||||
this.feeService = feeService;
|
||||
|
||||
|
@ -235,7 +235,7 @@ public class CompensationRequestManager implements PersistedDataHost, BsqBlockCh
|
|||
}
|
||||
|
||||
private boolean isInPhaseOrUnconfirmed(CompensationRequestPayload payload) {
|
||||
return readModel.getTxMap().get(payload.getTxId()) == null || daoPeriodService.isTxInPhase(payload.getTxId(), DaoPeriodService.Phase.COMPENSATION_REQUESTS);
|
||||
return bsqBlockChainReadModel.getTxMap().get(payload.getTxId()) == null || daoPeriodService.isTxInPhase(payload.getTxId(), DaoPeriodService.Phase.COMPENSATION_REQUESTS);
|
||||
}
|
||||
|
||||
public boolean isMine(CompensationRequest compensationRequest) {
|
||||
|
@ -336,7 +336,7 @@ public class CompensationRequestManager implements PersistedDataHost, BsqBlockCh
|
|||
pastRequests.setPredicate(request -> daoPeriodService.isTxInPastCycle(request.getPayload().getTxId()));
|
||||
activeRequests.setPredicate(compensationRequest -> {
|
||||
return daoPeriodService.isTxInCurrentCycle(compensationRequest.getPayload().getTxId()) ||
|
||||
(readModel.getTxMap().get(compensationRequest.getPayload().getTxId()) == null &&
|
||||
(bsqBlockChainReadModel.getTxMap().get(compensationRequest.getPayload().getTxId()) == null &&
|
||||
isMine(compensationRequest));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,16 +4,16 @@ import com.neemre.btcdcli4j.core.BitcoindException;
|
|||
import com.neemre.btcdcli4j.core.CommunicationException;
|
||||
import com.neemre.btcdcli4j.core.domain.Block;
|
||||
import io.bisq.common.proto.persistable.PersistenceProtoResolver;
|
||||
import io.bisq.core.dao.blockchain.ReadModel;
|
||||
import io.bisq.core.dao.blockchain.BsqBlockChainReadModel;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
|
||||
import io.bisq.core.dao.blockchain.exceptions.BsqBlockchainException;
|
||||
import io.bisq.core.dao.blockchain.vo.Tx;
|
||||
import io.bisq.core.dao.blockchain.vo.TxInput;
|
||||
import io.bisq.core.dao.blockchain.vo.TxOutput;
|
||||
import io.bisq.core.dao.blockchain.vo.util.TxIdIndexTuple;
|
||||
import io.bisq.core.dao.node.consensus.BsqTxVerification;
|
||||
import io.bisq.core.dao.node.consensus.IssuanceVerification;
|
||||
import io.bisq.core.dao.node.consensus.OpReturnVerification;
|
||||
import io.bisq.core.dao.node.consensus.BsqTxController;
|
||||
import io.bisq.core.dao.node.consensus.IssuanceController;
|
||||
import io.bisq.core.dao.node.consensus.OpReturnController;
|
||||
import io.bisq.core.dao.node.full.rpc.RpcService;
|
||||
import mockit.Expectations;
|
||||
import mockit.Injectable;
|
||||
|
@ -39,12 +39,12 @@ import static org.junit.Assert.assertTrue;
|
|||
@RunWith(JMockit.class)
|
||||
public class FullNodeParserTest {
|
||||
@Tested(availableDuringSetup = true)
|
||||
ReadModel readModel;
|
||||
BsqBlockChainReadModel bsqBlockChainReadModel;
|
||||
@Tested(fullyInitialized = true, availableDuringSetup = true)
|
||||
FullNodeParser fullNodeParser;
|
||||
|
||||
@Tested(fullyInitialized = true, availableDuringSetup = true)
|
||||
BsqTxVerification bsqTxVerification;
|
||||
BsqTxController bsqTxController;
|
||||
|
||||
@Injectable
|
||||
PersistenceProtoResolver persistenceProtoResolver;
|
||||
|
@ -58,9 +58,9 @@ public class FullNodeParserTest {
|
|||
@Injectable
|
||||
RpcService rpcService;
|
||||
@Injectable
|
||||
OpReturnVerification opReturnVerification;
|
||||
OpReturnController opReturnController;
|
||||
@Injectable
|
||||
IssuanceVerification issuanceVerification;
|
||||
IssuanceController issuanceController;
|
||||
|
||||
@Test
|
||||
public void testIsBsqTx() {
|
||||
|
@ -77,24 +77,24 @@ public class FullNodeParserTest {
|
|||
// 1) - null, 0 -> not BSQ transaction
|
||||
// 2) - 100, null -> BSQ transaction
|
||||
// 3) - 0, 100 -> BSQ transaction
|
||||
new Expectations(readModel) {{
|
||||
readModel.getSpendableTxOutput(new TxIdIndexTuple("tx1", 0));
|
||||
new Expectations(bsqBlockChainReadModel) {{
|
||||
bsqBlockChainReadModel.getSpendableTxOutput(new TxIdIndexTuple("tx1", 0));
|
||||
result = Optional.empty();
|
||||
result = Optional.of(new TxOutput(0, 100, "txout1", null, null, null, height));
|
||||
result = Optional.of(new TxOutput(0, 0, "txout1", null, null, null, height));
|
||||
|
||||
readModel.getSpendableTxOutput(new TxIdIndexTuple("tx1", 1));
|
||||
bsqBlockChainReadModel.getSpendableTxOutput(new TxIdIndexTuple("tx1", 1));
|
||||
result = Optional.of(new TxOutput(0, 0, "txout2", null, null, null, height));
|
||||
result = Optional.empty();
|
||||
result = Optional.of(new TxOutput(0, 100, "txout2", null, null, null, height));
|
||||
}};
|
||||
|
||||
// First time there is no BSQ value to spend so it's not a bsq transaction
|
||||
assertFalse(bsqTxVerification.isBsqTx(height, tx));
|
||||
assertFalse(bsqTxController.isBsqTx(height, tx));
|
||||
// Second time there is BSQ in the first txout
|
||||
assertTrue(bsqTxVerification.isBsqTx(height, tx));
|
||||
assertTrue(bsqTxController.isBsqTx(height, tx));
|
||||
// Third time there is BSQ in the second txout
|
||||
assertTrue(bsqTxVerification.isBsqTx(height, tx));
|
||||
assertTrue(bsqTxController.isBsqTx(height, tx));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -170,25 +170,25 @@ public class FullNodeParserTest {
|
|||
});
|
||||
|
||||
// Verify that the genesis tx has been added to the bsq blockchain with the correct issuance amount
|
||||
assertTrue(readModel.getGenesisTx() == genesisTx);
|
||||
assertTrue(readModel.getIssuedAmount().getValue() == issuance.getValue());
|
||||
assertTrue(bsqBlockChainReadModel.getGenesisTx() == genesisTx);
|
||||
assertTrue(bsqBlockChainReadModel.getIssuedAmount().getValue() == issuance.getValue());
|
||||
|
||||
// And that other txs are not added
|
||||
assertFalse(readModel.containsTx(cbId199));
|
||||
assertFalse(readModel.containsTx(cbId200));
|
||||
assertFalse(readModel.containsTx(cbId201));
|
||||
assertFalse(bsqBlockChainReadModel.containsTx(cbId199));
|
||||
assertFalse(bsqBlockChainReadModel.containsTx(cbId200));
|
||||
assertFalse(bsqBlockChainReadModel.containsTx(cbId201));
|
||||
|
||||
// But bsq txs are added
|
||||
assertTrue(readModel.containsTx(bsqTx1Id));
|
||||
TxOutput bsqOut1 = readModel.getSpendableTxOutput(bsqTx1Id, 0).get();
|
||||
assertTrue(bsqBlockChainReadModel.containsTx(bsqTx1Id));
|
||||
TxOutput bsqOut1 = bsqBlockChainReadModel.getSpendableTxOutput(bsqTx1Id, 0).get();
|
||||
assertTrue(bsqOut1.isUnspent());
|
||||
assertTrue(bsqOut1.getValue() == bsqTx1Value1);
|
||||
TxOutput bsqOut2 = readModel.getSpendableTxOutput(bsqTx1Id, 1).get();
|
||||
TxOutput bsqOut2 = bsqBlockChainReadModel.getSpendableTxOutput(bsqTx1Id, 1).get();
|
||||
assertTrue(bsqOut2.isUnspent());
|
||||
assertTrue(bsqOut2.getValue() == bsqTx1Value2);
|
||||
assertFalse(readModel.isTxOutputSpendable(genesisId, 0));
|
||||
assertTrue(readModel.isTxOutputSpendable(bsqTx1Id, 0));
|
||||
assertTrue(readModel.isTxOutputSpendable(bsqTx1Id, 1));
|
||||
assertFalse(bsqBlockChainReadModel.isTxOutputSpendable(genesisId, 0));
|
||||
assertTrue(bsqBlockChainReadModel.isTxOutputSpendable(bsqTx1Id, 0));
|
||||
assertTrue(bsqBlockChainReadModel.isTxOutputSpendable(bsqTx1Id, 1));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue