diff --git a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java index 859ad34cfa4..7e7af323c73 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java @@ -22,7 +22,6 @@ import bisq.core.dao.node.parser.exceptions.BlockHeightNotConnectingException; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Block; -import bisq.core.dao.state.model.blockchain.Tx; import bisq.common.app.DevEnv; @@ -31,7 +30,6 @@ import javax.inject.Inject; import java.util.LinkedList; -import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -106,14 +104,13 @@ public Block parseBlock(RawBlock rawBlock) throws BlockHashNotConnectingExceptio // one get resolved. // Lately there is a patter with 24 iterations observed long startTs = System.currentTimeMillis(); - List txList = block.getTxs(); rawBlock.getRawTxs().forEach(rawTx -> txParser.findTx(rawTx, genesisTxId, genesisBlockHeight, genesisTotalSupply) - .ifPresent(txList::add)); + .ifPresent(daoStateService::onNewTxForLastBlock)); log.info("Parsing {} transactions at block height {} took {} ms", rawBlock.getRawTxs().size(), blockHeight, System.currentTimeMillis() - startTs); diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateService.java b/core/src/main/java/bisq/core/dao/state/DaoStateService.java index 8cbfc5553d6..6a5a99e8f60 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateService.java @@ -35,8 +35,8 @@ import bisq.core.dao.state.model.governance.Issuance; import bisq.core.dao.state.model.governance.IssuanceType; import bisq.core.dao.state.model.governance.ParamChange; -import bisq.core.util.coin.BsqFormatter; import bisq.core.util.ParsingUtils; +import bisq.core.util.coin.BsqFormatter; import org.bitcoinj.core.Coin; @@ -47,6 +47,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeMap; @@ -115,6 +116,9 @@ public void applySnapshot(DaoState snapshot) { daoState.setChainHeight(snapshot.getChainHeight()); + daoState.getTxMap().clear(); + daoState.getTxMap().putAll(snapshot.getTxMap()); + daoState.getBlocks().clear(); daoState.getBlocks().addAll(snapshot.getBlocks()); @@ -226,7 +230,15 @@ public void onNewBlockWithEmptyTxs(Block block) { } } - // Third we get the onParseBlockComplete called after all rawTxs of blocks have been parsed + // Third we add each successfully parsed BSQ tx to the last block + public void onNewTxForLastBlock(Tx tx) { + getLastBlock().ifPresent(block -> { + block.getTxs().add(tx); + daoState.getTxMap().put(tx.getId(), tx); + }); + } + + // Fourth we get the onParseBlockComplete called after all rawTxs of blocks have been parsed public void onParseBlockComplete(Block block) { if (parseBlockChainComplete) log.info("Parse block completed: Block height {}, {} BSQ transactions.", block.getHeight(), block.getTxs().size()); @@ -348,16 +360,16 @@ public Stream getTxStream() { .flatMap(block -> block.getTxs().stream()); } - public TreeMap getTxMap() { - return new TreeMap<>(getTxStream().collect(Collectors.toMap(Tx::getId, tx -> tx))); + public Map getTxMap() { + return daoState.getTxMap(); } public Set getTxs() { - return getTxStream().collect(Collectors.toSet()); + return new HashSet<>(getTxMap().values()); } public Optional getTx(String txId) { - return getTxStream().filter(tx -> tx.getId().equals(txId)).findAny(); + return Optional.ofNullable(getTxMap().get(txId)); } public List getInvalidTxs() { diff --git a/core/src/main/java/bisq/core/dao/state/model/DaoState.java b/core/src/main/java/bisq/core/dao/state/model/DaoState.java index 0d98cbe996a..ef066cf99c6 100644 --- a/core/src/main/java/bisq/core/dao/state/model/DaoState.java +++ b/core/src/main/java/bisq/core/dao/state/model/DaoState.java @@ -19,6 +19,7 @@ import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.SpentInfo; +import bisq.core.dao.state.model.blockchain.Tx; import bisq.core.dao.state.model.blockchain.TxOutput; import bisq.core.dao.state.model.blockchain.TxOutputKey; import bisq.core.dao.state.model.governance.Cycle; @@ -28,16 +29,19 @@ import bisq.core.dao.state.model.governance.ParamChange; import bisq.common.proto.persistable.PersistablePayload; +import bisq.common.util.JsonExclude; import com.google.protobuf.Message; import javax.inject.Inject; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.function.Function; import java.util.stream.Collectors; import lombok.Getter; @@ -98,6 +102,11 @@ public static DaoState getClone(DaoState daoState) { @Getter private final List decryptedBallotsWithMeritsList; + // Transient data used only as an index - must be kept in sync with the block list + @Getter + @JsonExclude + private transient final Map txMap; // key is txId + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -114,7 +123,8 @@ public DaoState() { new TreeMap<>(), new ArrayList<>(), new ArrayList<>(), - new ArrayList<>() + new ArrayList<>(), + new HashMap<>() ); } @@ -132,7 +142,8 @@ private DaoState(int chainHeight, TreeMap issuanceMap, List paramChangeList, List evaluatedProposalList, - List decryptedBallotsWithMeritsList) { + List decryptedBallotsWithMeritsList, + Map txMap) { this.chainHeight = chainHeight; this.blocks = blocks; this.cycles = cycles; @@ -145,6 +156,8 @@ private DaoState(int chainHeight, this.paramChangeList = paramChangeList; this.evaluatedProposalList = evaluatedProposalList; this.decryptedBallotsWithMeritsList = decryptedBallotsWithMeritsList; + + this.txMap = txMap; } @Override @@ -176,6 +189,10 @@ private protobuf.DaoState.Builder getBsqStateBuilderExcludingBlocks() { } public static DaoState fromProto(protobuf.DaoState proto) { + Map txMap = proto.getBlocksList().stream() + .map(Block::fromProto) + .flatMap(block -> block.getTxs().stream()) + .collect(Collectors.toMap(Tx::getId, Function.identity(), (x, y) -> y, HashMap::new)); LinkedList blocks = proto.getBlocksList().stream() .map(Block::fromProto) .collect(Collectors.toCollection(LinkedList::new)); @@ -203,7 +220,8 @@ public static DaoState fromProto(protobuf.DaoState proto) { issuanceMap, paramChangeList, evaluatedProposalList, - decryptedBallotsWithMeritsList); + decryptedBallotsWithMeritsList, + txMap); } @@ -237,6 +255,7 @@ public String toString() { ",\n paramChangeList=" + paramChangeList + ",\n evaluatedProposalList=" + evaluatedProposalList + ",\n decryptedBallotsWithMeritsList=" + decryptedBallotsWithMeritsList + + ",\n txMap=" + txMap + "\n}"; } }