Support for persisted bsqState as resource file

- Move DecryptedBallotsWithMerits list and EvaluatedProposal list to
BsqState

- Use StoreService for handling persistence of snapshots
This commit is contained in:
Manfred Karrer 2018-10-16 23:21:11 -05:00
parent 8e46b1760f
commit a308469ac6
No known key found for this signature in database
GPG Key ID: 401250966A6B2C46
23 changed files with 398 additions and 402 deletions

View File

@ -936,8 +936,7 @@ message PersistableEnvelope {
MeritList merit_list = 23;
BondedRoleList bonded_role_list = 24;
RemovedAssetList removed_asset_list = 25;
EvaluatedProposalList evaluated_proposal_list = 26;
DecryptedBallotsWithMeritsList decrypted_ballots_with_merits_list = 27;
DaoStateStore dao_state_store = 28;
}
}
@ -1460,6 +1459,8 @@ message BsqState {
map<string, BaseTxOutput> confiscated_tx_output_map = 7;
map<string, SpentInfo> spent_info_map = 8;
repeated ParamChange param_change_list = 9;
repeated EvaluatedProposal evaluated_proposal_list = 10;
repeated DecryptedBallotsWithMerits decrypted_ballots_with_merits_list = 11;
}
message Issuance {
@ -1647,21 +1648,17 @@ message EvaluatedProposal {
int64 required_threshold = 4;
}
message EvaluatedProposalList {
repeated EvaluatedProposal evaluated_proposal = 1;
}
message DecryptedBallotsWithMerits {
bytes hash_of_blind_vote_list = 1;
string vote_reveal_tx_id = 2;
string blind_vote_tx_id = 3;
string blind_vote_tx_id = 2;
string vote_reveal_tx_id = 3;
int64 stake = 4;
BallotList ballot_list = 5;
MeritList merit_list = 6;
}
message DecryptedBallotsWithMeritsList {
repeated DecryptedBallotsWithMerits decrypted_ballots_with_merits = 1;
message DaoStateStore {
BsqState bsq_state = 1;
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -23,7 +23,6 @@ import bisq.core.dao.governance.ballot.BallotListService;
import bisq.core.dao.governance.blindvote.MyBlindVoteListService;
import bisq.core.dao.governance.myvote.MyVoteListService;
import bisq.core.dao.governance.role.BondedRolesService;
import bisq.core.dao.governance.voteresult.VoteResultService;
import bisq.core.filter.FilterManager;
import bisq.core.payment.AccountAgeWitnessService;
import bisq.core.trade.statistics.TradeStatisticsManager;
@ -53,8 +52,7 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P {
BallotListService ballotListService,
MyBlindVoteListService myBlindVoteListService,
BondedRolesService bondedRolesService,
AssetService assetService,
VoteResultService voteResultService) {
AssetService assetService) {
super(encryptionService,
keyRing,
p2PService,
@ -69,7 +67,6 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P {
persistedDataHosts.add(myBlindVoteListService);
persistedDataHosts.add(bondedRolesService);
persistedDataHosts.add(assetService);
persistedDataHosts.add(voteResultService);
}
@Override

View File

@ -65,8 +65,9 @@ import bisq.core.dao.node.parser.BlockParser;
import bisq.core.dao.node.parser.TxParser;
import bisq.core.dao.state.BsqState;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.DaoStateSnapshotService;
import bisq.core.dao.state.DaoStateStorageService;
import bisq.core.dao.state.GenesisTxInfo;
import bisq.core.dao.state.SnapshotManager;
import bisq.core.dao.state.period.CycleService;
import bisq.core.dao.state.period.PeriodService;
@ -103,7 +104,9 @@ public class DaoModule extends AppModule {
bind(GenesisTxInfo.class).in(Singleton.class);
bind(BsqState.class).in(Singleton.class);
bind(BsqStateService.class).in(Singleton.class);
bind(SnapshotManager.class).in(Singleton.class);
bind(DaoStateSnapshotService.class).in(Singleton.class);
bind(DaoStateStorageService.class).in(Singleton.class);
bind(ExportJsonFilesService.class).in(Singleton.class);
// Period

View File

@ -43,17 +43,17 @@ import lombok.extern.slf4j.Slf4j;
@Value
public class DecryptedBallotsWithMerits implements PersistablePayload {
private final byte[] hashOfBlindVoteList;
private final String voteRevealTxId; // not used yet but keep it for now
private final String blindVoteTxId; // not used yet but keep it for now
private final String blindVoteTxId;
private final String voteRevealTxId;
private final long stake;
private final BallotList ballotList;
private final MeritList meritList;
DecryptedBallotsWithMerits(byte[] hashOfBlindVoteList, String voteRevealTxId, String blindVoteTxId, long stake,
BallotList ballotList, MeritList meritList) {
public DecryptedBallotsWithMerits(byte[] hashOfBlindVoteList, String blindVoteTxId, String voteRevealTxId, long stake,
BallotList ballotList, MeritList meritList) {
this.hashOfBlindVoteList = hashOfBlindVoteList;
this.voteRevealTxId = voteRevealTxId;
this.blindVoteTxId = blindVoteTxId;
this.voteRevealTxId = voteRevealTxId;
this.stake = stake;
this.ballotList = ballotList;
this.meritList = meritList;
@ -66,20 +66,23 @@ public class DecryptedBallotsWithMerits implements PersistablePayload {
@Override
public PB.DecryptedBallotsWithMerits toProtoMessage() {
PB.DecryptedBallotsWithMerits.Builder builder = PB.DecryptedBallotsWithMerits.newBuilder()
return getBuilder().build();
}
public PB.DecryptedBallotsWithMerits.Builder getBuilder() {
return PB.DecryptedBallotsWithMerits.newBuilder()
.setHashOfBlindVoteList(ByteString.copyFrom(hashOfBlindVoteList))
.setVoteRevealTxId(voteRevealTxId)
.setBlindVoteTxId(blindVoteTxId)
.setVoteRevealTxId(voteRevealTxId)
.setStake(stake)
.setBallotList(ballotList.getBuilder())
.setMeritList(meritList.getBuilder());
return builder.build();
}
public static DecryptedBallotsWithMerits fromProto(PB.DecryptedBallotsWithMerits proto) {
return new DecryptedBallotsWithMerits(proto.getHashOfBlindVoteList().toByteArray(),
proto.getVoteRevealTxId(),
proto.getBlindVoteTxId(),
proto.getVoteRevealTxId(),
proto.getStake(),
BallotList.fromProto(proto.getBallotList()),
MeritList.fromProto(proto.getMeritList()));
@ -105,8 +108,8 @@ public class DecryptedBallotsWithMerits implements PersistablePayload {
public String toString() {
return "DecryptedBallotsWithMerits{" +
"\n hashOfBlindVoteList=" + Utilities.bytesAsHexString(hashOfBlindVoteList) +
",\n voteRevealTxId='" + voteRevealTxId + '\'' +
",\n blindVoteTxId='" + blindVoteTxId + '\'' +
",\n voteRevealTxId='" + voteRevealTxId + '\'' +
",\n stake=" + stake +
",\n ballotList=" + ballotList +
",\n meritList=" + meritList +

View File

@ -1,76 +0,0 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.dao.governance.voteresult;
import bisq.core.dao.governance.ConsensusCritical;
import bisq.common.proto.persistable.PersistableList;
import io.bisq.generated.protobuffer.PB;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.EqualsAndHashCode;
/**
* PersistableEnvelope wrapper for list of decryptedBallotsWithMerits.
*/
@EqualsAndHashCode(callSuper = true)
public class DecryptedBallotsWithMeritsList extends PersistableList<DecryptedBallotsWithMerits> implements ConsensusCritical {
public DecryptedBallotsWithMeritsList(List<DecryptedBallotsWithMerits> list) {
super(list);
}
public DecryptedBallotsWithMeritsList() {
super();
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public PB.PersistableEnvelope toProtoMessage() {
return PB.PersistableEnvelope.newBuilder().setDecryptedBallotsWithMeritsList(getBuilder()).build();
}
private PB.DecryptedBallotsWithMeritsList.Builder getBuilder() {
return PB.DecryptedBallotsWithMeritsList.newBuilder()
.addAllDecryptedBallotsWithMerits(getList().stream()
.map(DecryptedBallotsWithMerits::toProtoMessage)
.collect(Collectors.toList()));
}
public static DecryptedBallotsWithMeritsList fromProto(PB.DecryptedBallotsWithMeritsList proto) {
return new DecryptedBallotsWithMeritsList(new ArrayList<>(proto.getDecryptedBallotsWithMeritsList().stream()
.map(DecryptedBallotsWithMerits::fromProto)
.collect(Collectors.toList())));
}
@Override
public String toString() {
return "List of blindVoteTxId's in DecryptedBallotsWithMeritsList: " + getList().stream()
.map(DecryptedBallotsWithMerits::getBlindVoteTxId)
.collect(Collectors.toList());
}
}

View File

@ -29,6 +29,8 @@ import lombok.Value;
public class EvaluatedProposal implements PersistablePayload {
private final boolean isAccepted;
private final ProposalVoteResult proposalVoteResult;
//TODO remove - can be derived from params
private final long requiredQuorum;
private final long requiredThreshold;

View File

@ -1,76 +0,0 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.dao.governance.voteresult;
import bisq.core.dao.governance.ConsensusCritical;
import bisq.common.proto.persistable.PersistableList;
import io.bisq.generated.protobuffer.PB;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.EqualsAndHashCode;
/**
* PersistableEnvelope wrapper for list of evaluatedProposals.
*/
@EqualsAndHashCode(callSuper = true)
public class EvaluatedProposalList extends PersistableList<EvaluatedProposal> implements ConsensusCritical {
public EvaluatedProposalList(List<EvaluatedProposal> list) {
super(list);
}
public EvaluatedProposalList() {
super();
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public PB.PersistableEnvelope toProtoMessage() {
return PB.PersistableEnvelope.newBuilder().setEvaluatedProposalList(getBuilder()).build();
}
public PB.EvaluatedProposalList.Builder getBuilder() {
return PB.EvaluatedProposalList.newBuilder()
.addAllEvaluatedProposal(getList().stream()
.map(EvaluatedProposal::toProtoMessage)
.collect(Collectors.toList()));
}
public static EvaluatedProposalList fromProto(PB.EvaluatedProposalList proto) {
return new EvaluatedProposalList(new ArrayList<>(proto.getEvaluatedProposalList().stream()
.map(EvaluatedProposal::fromProto)
.collect(Collectors.toList())));
}
@Override
public String toString() {
return "List of proposalTxId's in EvaluatedProposalList: " + getList().stream()
.map(EvaluatedProposal::getProposalTxId)
.collect(Collectors.toList());
}
}

View File

@ -17,7 +17,6 @@
package bisq.core.dao.governance.voteresult;
import bisq.core.app.BisqEnvironment;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.governance.asset.AssetService;
import bisq.core.dao.governance.ballot.Ballot;
@ -56,8 +55,6 @@ import bisq.core.locale.CurrencyUtil;
import bisq.network.p2p.storage.P2PDataStorage;
import bisq.common.proto.persistable.PersistedDataHost;
import bisq.common.storage.Storage;
import bisq.common.util.Utilities;
import javax.inject.Inject;
@ -95,7 +92,7 @@ import static com.google.common.base.Preconditions.checkArgument;
* the missing blindVotes from the network.
*/
@Slf4j
public class VoteResultService implements BsqStateListener, DaoSetupService, PersistedDataHost {
public class VoteResultService implements BsqStateListener, DaoSetupService {
private final VoteRevealService voteRevealService;
private final ProposalListPresentation proposalListPresentation;
private final BsqStateService bsqStateService;
@ -109,13 +106,6 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
@Getter
private final ObservableList<VoteResultException> voteResultExceptions = FXCollections.observableArrayList();
private final Storage<EvaluatedProposalList> evaluatedProposalStorage;
private Storage<DecryptedBallotsWithMeritsList> decryptedBallotsWithMeritsStorage;
@Getter
private final EvaluatedProposalList evaluatedProposalList = new EvaluatedProposalList();
@Getter
private final DecryptedBallotsWithMeritsList decryptedBallotsWithMeritsList = new DecryptedBallotsWithMeritsList();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -131,9 +121,7 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
BondedRolesService bondedRolesService,
IssuanceService issuanceService,
AssetService assetService,
MissingDataRequestService missingDataRequestService,
Storage<EvaluatedProposalList> evaluatedProposalStorage,
Storage<DecryptedBallotsWithMeritsList> decryptedBallotsWithMeritsStorage) {
MissingDataRequestService missingDataRequestService) {
this.voteRevealService = voteRevealService;
this.proposalListPresentation = proposalListPresentation;
this.bsqStateService = bsqStateService;
@ -144,30 +132,6 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
this.issuanceService = issuanceService;
this.assetService = assetService;
this.missingDataRequestService = missingDataRequestService;
this.evaluatedProposalStorage = evaluatedProposalStorage;
this.decryptedBallotsWithMeritsStorage = decryptedBallotsWithMeritsStorage;
}
///////////////////////////////////////////////////////////////////////////////////////////
// PersistedDataHost
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void readPersisted() {
if (BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq()) {
EvaluatedProposalList persisted1 = evaluatedProposalStorage.initAndGetPersisted(evaluatedProposalList, 100);
if (persisted1 != null) {
evaluatedProposalList.clear();
evaluatedProposalList.addAll(persisted1.getList());
}
DecryptedBallotsWithMeritsList persisted2 = decryptedBallotsWithMeritsStorage.initAndGetPersisted(decryptedBallotsWithMeritsList, 100);
if (persisted2 != null) {
decryptedBallotsWithMeritsList.clear();
decryptedBallotsWithMeritsList.addAll(persisted2.getList());
}
}
}
@ -213,9 +177,8 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
if (isInVoteResultPhase(chainHeight)) {
Set<DecryptedBallotsWithMerits> decryptedBallotsWithMeritsSet = getDecryptedBallotsWithMeritsSet(chainHeight);
decryptedBallotsWithMeritsSet.stream()
.filter(e -> !decryptedBallotsWithMeritsList.getList().contains(e))
.forEach(decryptedBallotsWithMeritsList::add);
persistDecryptedBallotsWithMerits();
.filter(e -> !bsqStateService.getDecryptedBallotsWithMeritsList().contains(e))
.forEach(bsqStateService.getDecryptedBallotsWithMeritsList()::add);
if (!decryptedBallotsWithMeritsSet.isEmpty()) {
// From the decryptedBallotsWithMerits we create a map with the hash of the blind vote list as key and the
@ -244,9 +207,8 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
applyAcceptedProposals(acceptedEvaluatedProposals, chainHeight);
evaluatedProposals.stream()
.filter(e -> !evaluatedProposalList.getList().contains(e))
.forEach(evaluatedProposalList::add);
persistEvaluatedProposals();
.filter(e -> !bsqStateService.getEvaluatedProposalList().contains(e))
.forEach(bsqStateService.getEvaluatedProposalList()::add);
log.info("processAllVoteResults completed");
} else {
log.warn("Our list of received blind votes do not match the list from the majority of voters.");
@ -303,8 +265,6 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
Tx blindVoteTx = VoteResultConsensus.getBlindVoteTx(blindVoteStakeOutput, bsqStateService, periodService, chainHeight);
String blindVoteTxId = blindVoteTx.getId();
// Here we deal with eventual consistency of the p2p network data!
// TODO make more clear we are in p2p domain now
List<BlindVote> blindVoteList = BlindVoteConsensus.getSortedBlindVoteListOfCycle(blindVoteListService);
Optional<BlindVote> optionalBlindVote = blindVoteList.stream()
.filter(blindVote -> blindVote.getTxId().equals(blindVoteTxId))
@ -318,9 +278,8 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
// voteWithProposalTxIdList and create a ballot list with the proposal and the vote from
// the voteWithProposalTxIdList
BallotList ballotList = createBallotList(voteWithProposalTxIdList);
return new DecryptedBallotsWithMerits(hashOfBlindVoteList, voteRevealTxId, blindVoteTxId, blindVoteStake, ballotList, meritList);
return new DecryptedBallotsWithMerits(hashOfBlindVoteList, blindVoteTxId, voteRevealTxId, blindVoteStake, ballotList, meritList);
} catch (VoteResultException.MissingBallotException missingBallotException) {
//TODO handle case that we are missing proposals
log.warn("We are missing proposals to create the vote result: " + missingBallotException.toString());
missingDataRequestService.addVoteResultException(missingBallotException);
voteResultExceptions.add(missingBallotException);
@ -331,8 +290,7 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
return null;
}
} else {
//TODO handle recovering
log.warn("We have a blindVoteTx but we do not have the corresponding blindVote in our local list.\n" +
log.warn("We have a blindVoteTx but we do not have the corresponding blindVote payload in our local database.\n" +
"That can happen if the blindVote item was not properly broadcast. We will go on " +
"and see if that blindVote was part of the majority data view. If so we should " +
"recover the missing blind vote by a request to our peers. blindVoteTxId={}", blindVoteTxId);
@ -738,14 +696,6 @@ public class VoteResultService implements BsqStateListener, DaoSetupService, Per
return periodService.getFirstBlockOfPhase(chainHeight, DaoPhase.Phase.RESULT) == chainHeight;
}
private void persistEvaluatedProposals() {
evaluatedProposalStorage.queueUpForSave(20);
}
private void persistDecryptedBallotsWithMerits() {
decryptedBallotsWithMeritsStorage.queueUpForSave(20);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Inner classes

View File

@ -20,7 +20,7 @@ package bisq.core.dao.node;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.node.parser.BlockParser;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.SnapshotManager;
import bisq.core.dao.state.DaoStateSnapshotService;
import bisq.core.dao.state.blockchain.RawBlock;
import bisq.network.p2p.P2PService;
@ -45,7 +45,7 @@ public abstract class BsqNode implements DaoSetupService {
protected final BsqStateService bsqStateService;
private final String genesisTxId;
private final int genesisBlockHeight;
private final SnapshotManager snapshotManager;
private final DaoStateSnapshotService daoStateSnapshotService;
private final P2PServiceListener p2PServiceListener;
protected boolean parseBlockchainComplete;
protected boolean p2pNetworkReady;
@ -60,11 +60,11 @@ public abstract class BsqNode implements DaoSetupService {
@Inject
public BsqNode(BlockParser blockParser,
BsqStateService bsqStateService,
SnapshotManager snapshotManager,
DaoStateSnapshotService daoStateSnapshotService,
P2PService p2PService) {
this.blockParser = blockParser;
this.bsqStateService = bsqStateService;
this.snapshotManager = snapshotManager;
this.daoStateSnapshotService = daoStateSnapshotService;
this.p2PService = p2PService;
genesisTxId = bsqStateService.getGenesisTxId();
@ -201,6 +201,6 @@ public abstract class BsqNode implements DaoSetupService {
///////////////////////////////////////////////////////////////////////////////////////////
private void applySnapshot() {
snapshotManager.applySnapshot();
daoStateSnapshotService.applySnapshot();
}
}

View File

@ -23,7 +23,7 @@ import bisq.core.dao.node.json.ExportJsonFilesService;
import bisq.core.dao.node.parser.BlockParser;
import bisq.core.dao.node.parser.exceptions.BlockNotConnectingException;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.SnapshotManager;
import bisq.core.dao.state.DaoStateSnapshotService;
import bisq.core.dao.state.blockchain.Block;
import bisq.network.p2p.P2PService;
@ -61,12 +61,12 @@ public class FullNode extends BsqNode {
@Inject
public FullNode(BlockParser blockParser,
BsqStateService bsqStateService,
SnapshotManager snapshotManager,
DaoStateSnapshotService daoStateSnapshotService,
P2PService p2PService,
RpcService rpcService,
ExportJsonFilesService exportJsonFilesService,
FullNodeNetworkService fullNodeNetworkService) {
super(blockParser, bsqStateService, snapshotManager, p2PService);
super(blockParser, bsqStateService, daoStateSnapshotService, p2PService);
this.rpcService = rpcService;
this.exportJsonFilesService = exportJsonFilesService;

View File

@ -24,7 +24,7 @@ import bisq.core.dao.node.messages.NewBlockBroadcastMessage;
import bisq.core.dao.node.parser.BlockParser;
import bisq.core.dao.node.parser.exceptions.BlockNotConnectingException;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.SnapshotManager;
import bisq.core.dao.state.DaoStateSnapshotService;
import bisq.core.dao.state.blockchain.RawBlock;
import bisq.network.p2p.P2PService;
@ -56,10 +56,10 @@ public class LiteNode extends BsqNode {
@Inject
public LiteNode(BlockParser blockParser,
BsqStateService bsqStateService,
SnapshotManager snapshotManager,
DaoStateSnapshotService daoStateSnapshotService,
P2PService p2PService,
LiteNodeNetworkService liteNodeNetworkService) {
super(blockParser, bsqStateService, snapshotManager, p2PService);
super(blockParser, bsqStateService, daoStateSnapshotService, p2PService);
this.liteNodeNetworkService = liteNodeNetworkService;
}

View File

@ -17,6 +17,8 @@
package bisq.core.dao.state;
import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMerits;
import bisq.core.dao.governance.voteresult.EvaluatedProposal;
import bisq.core.dao.state.blockchain.Block;
import bisq.core.dao.state.blockchain.SpentInfo;
import bisq.core.dao.state.blockchain.TxOutput;
@ -25,7 +27,7 @@ import bisq.core.dao.state.governance.Issuance;
import bisq.core.dao.state.governance.ParamChange;
import bisq.core.dao.state.period.Cycle;
import bisq.common.proto.persistable.PersistableEnvelope;
import bisq.common.proto.persistable.PersistablePayload;
import io.bisq.generated.protobuffer.PB;
@ -47,9 +49,11 @@ import lombok.extern.slf4j.Slf4j;
/**
* Root class for mutable state of the DAO.
* Holds both blockchain data as well as data derived from the governance process (voting).
* <p>
* One BSQ block with empty txs adds 152 bytes which results in about 8 MB/year
*/
@Slf4j
public class BsqState implements PersistableEnvelope {
public class BsqState implements PersistablePayload {
///////////////////////////////////////////////////////////////////////////////////////////
// Fields
@ -78,6 +82,14 @@ public class BsqState implements PersistableEnvelope {
@Getter
private final List<ParamChange> paramChangeList;
// Vote result data
// All evaluated proposals which get added at the result phase
@Getter
private final List<EvaluatedProposal> evaluatedProposalList;
// All voting data which get added at the result phase
@Getter
private final List<DecryptedBallotsWithMerits> decryptedBallotsWithMeritsList;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -93,6 +105,8 @@ public class BsqState implements PersistableEnvelope {
new HashMap<>(),
new HashMap<>(),
new HashMap<>(),
new ArrayList<>(),
new ArrayList<>(),
new ArrayList<>()
);
}
@ -110,7 +124,9 @@ public class BsqState implements PersistableEnvelope {
Map<TxOutputKey, SpentInfo> spentInfoMap,
Map<TxOutputKey, TxOutput> confiscatedTxOutputMap,
Map<String, Issuance> issuanceMap,
List<ParamChange> paramChangeList) {
List<ParamChange> paramChangeList,
List<EvaluatedProposal> evaluatedProposalList,
List<DecryptedBallotsWithMerits> decryptedBallotsWithMeritsList) {
this.chainHeight = chainHeight;
this.blocks = blocks;
this.cycles = cycles;
@ -122,14 +138,16 @@ public class BsqState implements PersistableEnvelope {
this.confiscatedTxOutputMap = confiscatedTxOutputMap;
this.issuanceMap = issuanceMap;
this.paramChangeList = paramChangeList;
this.evaluatedProposalList = evaluatedProposalList;
this.decryptedBallotsWithMeritsList = decryptedBallotsWithMeritsList;
}
@Override
public Message toProtoMessage() {
return PB.PersistableEnvelope.newBuilder().setBsqState(getBsqStateBuilder()).build();
return getBsqStateBuilder().build();
}
private PB.BsqState.Builder getBsqStateBuilder() {
public PB.BsqState.Builder getBsqStateBuilder() {
final PB.BsqState.Builder builder = PB.BsqState.newBuilder();
builder.setChainHeight(chainHeight)
.addAllBlocks(blocks.stream().map(Block::toProtoMessage).collect(Collectors.toList()))
@ -144,15 +162,17 @@ public class BsqState implements PersistableEnvelope {
.collect(Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toProtoMessage())))
.putAllIssuanceMap(issuanceMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toProtoMessage())))
.addAllParamChangeList(paramChangeList.stream().map(ParamChange::toProtoMessage).collect(Collectors.toList()));
.addAllParamChangeList(paramChangeList.stream().map(ParamChange::toProtoMessage).collect(Collectors.toList()))
.addAllEvaluatedProposalList(evaluatedProposalList.stream().map(EvaluatedProposal::toProtoMessage).collect(Collectors.toList()))
.addAllDecryptedBallotsWithMeritsList(decryptedBallotsWithMeritsList.stream().map(DecryptedBallotsWithMerits::toProtoMessage).collect(Collectors.toList()));
return builder;
}
public static PersistableEnvelope fromProto(PB.BsqState proto) {
public static BsqState fromProto(PB.BsqState proto) {
LinkedList<Block> blocks = proto.getBlocksList().stream()
.map(Block::fromProto)
.collect(Collectors.toCollection(LinkedList::new));
final LinkedList<Cycle> cycles = proto.getCyclesList().stream()
LinkedList<Cycle> cycles = proto.getCyclesList().stream()
.map(Cycle::fromProto).collect(Collectors.toCollection(LinkedList::new));
Map<TxOutputKey, TxOutput> unspentTxOutputMap = proto.getUnspentTxOutputMapMap().entrySet().stream()
.collect(Collectors.toMap(e -> TxOutputKey.getKeyFromString(e.getKey()), e -> TxOutput.fromProto(e.getValue())));
@ -164,8 +184,12 @@ public class BsqState implements PersistableEnvelope {
.collect(Collectors.toMap(e -> TxOutputKey.getKeyFromString(e.getKey()), e -> TxOutput.fromProto(e.getValue())));
Map<String, Issuance> issuanceMap = proto.getIssuanceMapMap().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> Issuance.fromProto(e.getValue())));
final List<ParamChange> paramChangeList = proto.getParamChangeListList().stream()
List<ParamChange> paramChangeList = proto.getParamChangeListList().stream()
.map(ParamChange::fromProto).collect(Collectors.toCollection(ArrayList::new));
List<EvaluatedProposal> evaluatedProposalList = proto.getEvaluatedProposalListList().stream()
.map(EvaluatedProposal::fromProto).collect(Collectors.toCollection(ArrayList::new));
List<DecryptedBallotsWithMerits> decryptedBallotsWithMeritsList = proto.getDecryptedBallotsWithMeritsListList().stream()
.map(DecryptedBallotsWithMerits::fromProto).collect(Collectors.toCollection(ArrayList::new));
return new BsqState(proto.getChainHeight(),
blocks,
cycles,
@ -174,7 +198,9 @@ public class BsqState implements PersistableEnvelope {
spentInfoMap,
confiscatedTxOutputMap,
issuanceMap,
paramChangeList);
paramChangeList,
evaluatedProposalList,
decryptedBallotsWithMeritsList);
}
@ -187,10 +213,10 @@ public class BsqState implements PersistableEnvelope {
}
BsqState getClone() {
return (BsqState) BsqState.fromProto(getBsqStateBuilder().build());
return BsqState.fromProto(getBsqStateBuilder().build());
}
BsqState getClone(BsqState snapshotCandidate) {
return (BsqState) BsqState.fromProto(snapshotCandidate.getBsqStateBuilder().build());
return BsqState.fromProto(snapshotCandidate.getBsqStateBuilder().build());
}
}

View File

@ -20,6 +20,8 @@ package bisq.core.dao.state;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.bonding.BondingConsensus;
import bisq.core.dao.governance.role.BondedRole;
import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMerits;
import bisq.core.dao.governance.voteresult.EvaluatedProposal;
import bisq.core.dao.state.blockchain.Block;
import bisq.core.dao.state.blockchain.SpentInfo;
import bisq.core.dao.state.blockchain.Tx;
@ -55,6 +57,9 @@ import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Provides access methods to BsqState data.
*/
@Slf4j
public class BsqStateService implements DaoSetupService {
private final BsqState bsqState;
@ -114,12 +119,22 @@ public class BsqStateService implements DaoSetupService {
bsqState.getParamChangeList().clear();
bsqState.getParamChangeList().addAll(snapshot.getParamChangeList());
bsqState.getEvaluatedProposalList().clear();
bsqState.getEvaluatedProposalList().addAll(snapshot.getEvaluatedProposalList());
bsqState.getDecryptedBallotsWithMeritsList().clear();
bsqState.getDecryptedBallotsWithMeritsList().addAll(snapshot.getDecryptedBallotsWithMeritsList());
}
public BsqState getClone() {
return bsqState.getClone();
}
BsqState getClone(BsqState snapshotCandidate) {
return bsqState.getClone(snapshotCandidate);
}
///////////////////////////////////////////////////////////////////////////////////////////
// ChainHeight
@ -196,8 +211,8 @@ public class BsqStateService implements DaoSetupService {
* Whether specified block hash belongs to a block we already know about.
*
* @param blockHash The hash of a {@link Block}.
* @return True if the hash belongs to a {@link Block} we know about, otherwise
* {@code false}.
* @return True if the hash belongs to a {@link Block} we know about, otherwise
* {@code false}.
*/
public boolean isBlockHashKnown(String blockHash) {
// TODO(chirhonul): If performance of O(n) time in number of blocks becomes an issue,
@ -772,6 +787,19 @@ public class BsqStateService implements DaoSetupService {
}
///////////////////////////////////////////////////////////////////////////////////////////
// Vote result data
///////////////////////////////////////////////////////////////////////////////////////////
public List<EvaluatedProposal> getEvaluatedProposalList() {
return bsqState.getEvaluatedProposalList();
}
public List<DecryptedBallotsWithMerits> getDecryptedBallotsWithMeritsList() {
return bsqState.getDecryptedBallotsWithMeritsList();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Listeners
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -19,48 +19,42 @@ package bisq.core.dao.state;
import bisq.core.dao.state.blockchain.Block;
import bisq.common.proto.persistable.PersistenceProtoResolver;
import bisq.common.storage.Storage;
import javax.inject.Inject;
import javax.inject.Named;
import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.util.LinkedList;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Manages snapshots of BsqState.
*
* One BSQ block with empty txs adds 152 bytes which results in about 8 MB/year
* Manages periodical snapshots of the BsqState.
* At startup we apply a snapshot if available.
* At each trigger height we persist the latest snapshot candidate and set the current bsqState as new candidate.
* The trigger height is determined by the SNAPSHOT_GRID. The latest persisted snapshot is min. the height of
* SNAPSHOT_GRID old not less than 2 times the SNAPSHOT_GRID old.
*/
@Slf4j
public class SnapshotManager implements BsqStateListener {
private static final int SNAPSHOT_GRID = 100;
public class DaoStateSnapshotService implements BsqStateListener {
private static final int SNAPSHOT_GRID = 10;
private final BsqState bsqState;
private final BsqStateService bsqStateService;
private final GenesisTxInfo genesisTxInfo;
private final Storage<BsqState> storage;
private final DaoStateStorageService daoStateStorageService;
private BsqState snapshotCandidate;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public SnapshotManager(BsqState bsqState,
BsqStateService bsqStateService,
PersistenceProtoResolver persistenceProtoResolver,
GenesisTxInfo genesisTxInfo,
@Named(Storage.STORAGE_DIR) File storageDir) {
this.bsqState = bsqState;
public DaoStateSnapshotService(BsqStateService bsqStateService,
GenesisTxInfo genesisTxInfo,
DaoStateStorageService daoStateStorageService) {
this.bsqStateService = bsqStateService;
this.genesisTxInfo = genesisTxInfo;
storage = new Storage<>(storageDir, persistenceProtoResolver);
this.daoStateStorageService = daoStateStorageService;
this.bsqStateService.addBsqStateListener(this);
}
@ -77,24 +71,33 @@ public class SnapshotManager implements BsqStateListener {
@Override
public void onParseTxsComplete(Block block) {
int chainHeight = block.getHeight();
// Either we don't have a snapshot candidate yet, or if we have one the height at that snapshot candidate must be
// different to our current height.
boolean noSnapshotCandidateOrDifferentHeight = snapshotCandidate == null || snapshotCandidate.getChainHeight() != chainHeight;
if (isSnapshotHeight(chainHeight) &&
(snapshotCandidate == null ||
snapshotCandidate.getChainHeight() != chainHeight)) {
isValidHeight(bsqStateService.getBlocks().getLast().getHeight()) &&
noSnapshotCandidateOrDifferentHeight) {
// At trigger event we store the latest snapshotCandidate to disc
if (snapshotCandidate != null) {
// We clone because storage is in a threaded context
final BsqState cloned = bsqState.getClone(snapshotCandidate);
if (cloned.getBlocks().getLast().getHeight() >= genesisTxInfo.getGenesisBlockHeight())
storage.queueUpForSave(cloned);
log.info("Saved snapshotCandidate to Disc at height " + chainHeight);
// We clone because storage is in a threaded context and we set the snapshotCandidate to our current
// state in the next step
BsqState cloned = bsqStateService.getClone(snapshotCandidate);
daoStateStorageService.persist(cloned);
log.info("Saved snapshotCandidate with height {} to Disc at height {} ",
snapshotCandidate.getChainHeight(), chainHeight);
}
// Now we clone and keep it in memory for the next trigger
snapshotCandidate = bsqState.getClone();
// don't access cloned anymore with methods as locks are transient!
// Now we clone and keep it in memory for the next trigger event
snapshotCandidate = bsqStateService.getClone();
log.info("Cloned new snapshotCandidate at height " + chainHeight);
}
}
private boolean isValidHeight(int heightOfLastBlock) {
return heightOfLastBlock >= genesisTxInfo.getGenesisBlockHeight();
}
@Override
public void onParseBlockChainComplete() {
}
@ -105,14 +108,17 @@ public class SnapshotManager implements BsqStateListener {
///////////////////////////////////////////////////////////////////////////////////////////
public void applySnapshot() {
checkNotNull(storage, "storage must not be null");
BsqState persisted = storage.initAndGetPersisted(bsqState, 100);
BsqState persisted = daoStateStorageService.getPersistedBsqState();
if (persisted != null) {
log.info("applySnapshot persisted.chainHeight=" + new LinkedList<>(persisted.getBlocks()).getLast().getHeight());
if (persisted.getBlocks().getLast().getHeight() >= genesisTxInfo.getGenesisBlockHeight())
bsqStateService.applySnapshot(persisted);
LinkedList<Block> blocks = persisted.getBlocks();
if (!blocks.isEmpty()) {
int heightOfLastBlock = blocks.getLast().getHeight();
log.info("applySnapshot from persisted bsqState with height of last block {}", heightOfLastBlock);
if (isValidHeight(heightOfLastBlock))
bsqStateService.applySnapshot(persisted);
}
} else {
log.info("Try to apply snapshot but no stored snapshot available");
log.info("Try to apply snapshot but no stored snapshot available. That is expected at first blocks.");
}
}
@ -132,6 +138,6 @@ public class SnapshotManager implements BsqStateListener {
}
private boolean isSnapshotHeight(int height) {
return isSnapshotHeight(bsqStateService.getGenesisBlockHeight(), height, SNAPSHOT_GRID);
return isSnapshotHeight(genesisTxInfo.getGenesisBlockHeight(), height, SNAPSHOT_GRID);
}
}

View File

@ -0,0 +1,85 @@
/*
* This file is part of Bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.dao.state;
import bisq.network.p2p.storage.persistence.ResourceDataStoreService;
import bisq.network.p2p.storage.persistence.StoreService;
import bisq.common.storage.Storage;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import lombok.extern.slf4j.Slf4j;
/**
* Manages persistence of the bsqState.
*/
@Slf4j
public class DaoStateStorageService extends StoreService<DaoStateStore> {
private static final String FILE_NAME = "DaoStateStore";
private BsqState bsqState;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public DaoStateStorageService(ResourceDataStoreService resourceDataStoreService,
BsqState bsqState,
@Named(Storage.STORAGE_DIR) File storageDir,
Storage<DaoStateStore> daoSnapshotStorage) {
super(storageDir, daoSnapshotStorage);
this.bsqState = bsqState;
resourceDataStoreService.addService(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public String getFileName() {
return FILE_NAME;
}
public void persist(BsqState bsqState) {
store.setBsqState(bsqState);
storage.queueUpForSave(store);
}
public BsqState getPersistedBsqState() {
return store.getBsqState();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
@Override
protected DaoStateStore createStore() {
return new DaoStateStore(bsqState.getClone());
}
}

View File

@ -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 bisq.core.dao.state;
import bisq.common.proto.persistable.PersistableEnvelope;
import io.bisq.generated.protobuffer.PB;
import com.google.protobuf.Message;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
public class DaoStateStore implements PersistableEnvelope {
// BsqState is always a clone and must not be used for read access beside initial read from disc when we apply
// the snapshot!
@Nullable
@Getter
@Setter
BsqState bsqState;
DaoStateStore(BsqState bsqState) {
this.bsqState = bsqState;
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
public Message toProtoMessage() {
checkNotNull(bsqState, "bsqState must not be null when toProtoMessage is invoked");
PB.DaoStateStore.Builder builder = PB.DaoStateStore.newBuilder()
.setBsqState(bsqState.getBsqStateBuilder());
return PB.PersistableEnvelope.newBuilder()
.setDaoStateStore(builder)
.build();
}
public static PersistableEnvelope fromProto(PB.DaoStateStore proto) {
return new DaoStateStore(BsqState.fromProto(proto.getBsqState()));
}
}

View File

@ -30,9 +30,7 @@ import bisq.core.dao.governance.proposal.MyProposalList;
import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStore;
import bisq.core.dao.governance.proposal.storage.temp.TempProposalStore;
import bisq.core.dao.governance.role.BondedRoleList;
import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMeritsList;
import bisq.core.dao.governance.voteresult.EvaluatedProposalList;
import bisq.core.dao.state.BsqState;
import bisq.core.dao.state.DaoStateStore;
import bisq.core.payment.AccountAgeWitnessStore;
import bisq.core.payment.PaymentAccountList;
import bisq.core.proto.CoreProtoResolver;
@ -121,8 +119,6 @@ public class CorePersistenceProtoResolver extends CoreProtoResolver implements P
return ProposalStore.fromProto(proto.getProposalStore());
case TEMP_PROPOSAL_STORE:
return TempProposalStore.fromProto(proto.getTempProposalStore(), networkProtoResolver);
case BSQ_STATE:
return BsqState.fromProto(proto.getBsqState());
case MY_PROPOSAL_LIST:
return MyProposalList.fromProto(proto.getMyProposalList());
case BALLOT_LIST:
@ -137,10 +133,8 @@ public class CorePersistenceProtoResolver extends CoreProtoResolver implements P
return BondedRoleList.fromProto(proto.getBondedRoleList());
case REMOVED_ASSET_LIST:
return RemovedAssetsList.fromProto(proto.getRemovedAssetList());
case EVALUATED_PROPOSAL_LIST:
return EvaluatedProposalList.fromProto(proto.getEvaluatedProposalList());
case DECRYPTED_BALLOTS_WITH_MERITS_LIST:
return DecryptedBallotsWithMeritsList.fromProto(proto.getDecryptedBallotsWithMeritsList());
case DAO_STATE_STORE:
return DaoStateStore.fromProto(proto.getDaoStateStore());
default:
throw new ProtobufferRuntimeException("Unknown proto message case(PB.PersistableEnvelope). " +
"messageCase=" + proto.getMessageCase() + "; proto raw data=" + proto.toString());

View File

@ -26,7 +26,6 @@ import bisq.core.dao.governance.blindvote.MyBlindVoteListService;
import bisq.core.dao.governance.myvote.MyVoteListService;
import bisq.core.dao.governance.proposal.MyProposalListService;
import bisq.core.dao.governance.role.BondedRolesService;
import bisq.core.dao.governance.voteresult.VoteResultService;
import bisq.core.offer.OpenOfferManager;
import bisq.core.trade.TradeManager;
import bisq.core.trade.closed.ClosedTradableManager;
@ -70,7 +69,6 @@ public class CorePersistedDataHost {
persistedDataHosts.add(injector.getInstance(MyProposalListService.class));
persistedDataHosts.add(injector.getInstance(BondedRolesService.class));
persistedDataHosts.add(injector.getInstance(AssetService.class));
persistedDataHosts.add(injector.getInstance(VoteResultService.class));
}
return persistedDataHosts;
}

View File

@ -0,0 +1,76 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.dao.state;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.powermock.api.mockito.PowerMockito.mock;
@RunWith(PowerMockRunner.class)
@PrepareForTest({BsqStateService.class, GenesisTxInfo.class, DaoStateStorageService.class})
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"})
public class DaoStateSnapshotServiceTest {
private DaoStateSnapshotService daoStateSnapshotService;
@Before
public void setup() {
daoStateSnapshotService = new DaoStateSnapshotService(mock(BsqStateService.class),
mock(GenesisTxInfo.class),
mock(DaoStateStorageService.class));
}
@Test
public void testGetSnapshotHeight() {
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 0, 10));
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 100, 10));
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 102, 10));
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 119, 10));
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 120, 10));
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 121, 10));
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 130, 10));
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 139, 10));
assertEquals(130, daoStateSnapshotService.getSnapshotHeight(102, 140, 10));
assertEquals(130, daoStateSnapshotService.getSnapshotHeight(102, 141, 10));
assertEquals(990, daoStateSnapshotService.getSnapshotHeight(102, 1000, 10));
}
@Test
public void testSnapshotHeight() {
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 0, 10));
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 80, 10));
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 90, 10));
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 100, 10));
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 119, 10));
assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 120, 10));
assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 130, 10));
assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 140, 10));
assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 200, 10));
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 201, 10));
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 199, 10));
}
}

View File

@ -1,82 +0,0 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.dao.state;
import bisq.common.proto.persistable.PersistenceProtoResolver;
import java.io.File;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.powermock.api.mockito.PowerMockito.mock;
@RunWith(PowerMockRunner.class)
@PrepareForTest({BsqState.class, BsqStateService.class, PersistenceProtoResolver.class, File.class})
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"})
public class SnapshotManagerTest {
private SnapshotManager snapshotManager;
@Before
public void setup() {
snapshotManager = new SnapshotManager(mock(BsqState.class),
mock(BsqStateService.class),
mock(PersistenceProtoResolver.class),
mock(GenesisTxInfo.class),
mock(File.class));
}
@Test
public void testGetSnapshotHeight() {
assertEquals(120, snapshotManager.getSnapshotHeight(102, 0, 10));
assertEquals(120, snapshotManager.getSnapshotHeight(102, 100, 10));
assertEquals(120, snapshotManager.getSnapshotHeight(102, 102, 10));
assertEquals(120, snapshotManager.getSnapshotHeight(102, 119, 10));
assertEquals(120, snapshotManager.getSnapshotHeight(102, 120, 10));
assertEquals(120, snapshotManager.getSnapshotHeight(102, 121, 10));
assertEquals(120, snapshotManager.getSnapshotHeight(102, 130, 10));
assertEquals(120, snapshotManager.getSnapshotHeight(102, 139, 10));
assertEquals(130, snapshotManager.getSnapshotHeight(102, 140, 10));
assertEquals(130, snapshotManager.getSnapshotHeight(102, 141, 10));
assertEquals(990, snapshotManager.getSnapshotHeight(102, 1000, 10));
}
@Test
public void testSnapshotHeight() {
assertFalse(snapshotManager.isSnapshotHeight(102, 0, 10));
assertFalse(snapshotManager.isSnapshotHeight(102, 80, 10));
assertFalse(snapshotManager.isSnapshotHeight(102, 90, 10));
assertFalse(snapshotManager.isSnapshotHeight(102, 100, 10));
assertFalse(snapshotManager.isSnapshotHeight(102, 119, 10));
assertTrue(snapshotManager.isSnapshotHeight(102, 120, 10));
assertTrue(snapshotManager.isSnapshotHeight(102, 130, 10));
assertTrue(snapshotManager.isSnapshotHeight(102, 140, 10));
assertTrue(snapshotManager.isSnapshotHeight(102, 200, 10));
assertFalse(snapshotManager.isSnapshotHeight(102, 201, 10));
assertFalse(snapshotManager.isSnapshotHeight(102, 199, 10));
}
}

View File

@ -44,8 +44,8 @@ import bisq.core.dao.governance.ballot.vote.Vote;
import bisq.core.dao.governance.myvote.MyVote;
import bisq.core.dao.governance.proposal.Proposal;
import bisq.core.dao.governance.voteresult.EvaluatedProposal;
import bisq.core.dao.governance.voteresult.VoteResultService;
import bisq.core.dao.state.BsqStateListener;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.blockchain.Block;
import bisq.core.dao.state.period.DaoPhase;
import bisq.core.locale.Res;
@ -103,7 +103,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private final DaoFacade daoFacade;
private final BsqWalletService bsqWalletService;
private final PhasesView phasesView;
private final VoteResultService voteResultService;
private final BsqStateService bsqStateService;
private final BsqFormatter bsqFormatter;
private final BSFormatter btcFormatter;
@ -142,13 +142,13 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private ProposalsView(DaoFacade daoFacade,
BsqWalletService bsqWalletService,
PhasesView phasesView,
VoteResultService voteResultService,
BsqStateService bsqStateService,
BsqFormatter bsqFormatter,
BSFormatter btcFormatter) {
this.daoFacade = daoFacade;
this.bsqWalletService = bsqWalletService;
this.phasesView = phasesView;
this.voteResultService = voteResultService;
this.bsqStateService = bsqStateService;
this.bsqFormatter = bsqFormatter;
this.btcFormatter = btcFormatter;
}
@ -418,7 +418,7 @@ public class ProposalsView extends ActivatableView<GridPane, Void> implements Bs
private void onSelectProposal(ProposalsListItem item) {
selectedItem = item;
if (selectedItem != null) {
EvaluatedProposal evaluatedProposal = voteResultService.getEvaluatedProposalList().stream()
EvaluatedProposal evaluatedProposal = bsqStateService.getEvaluatedProposalList().stream()
.filter(e -> daoFacade.isTxInCorrectCycle(e.getProposal().getTxId(),
daoFacade.getChainHeight()))
.filter(e -> e.getProposalTxId().equals(selectedItem.getProposal().getTxId()))

View File

@ -245,11 +245,11 @@ public class VoteResultView extends ActivatableView<GridPane, Void> implements B
.filter(proposal -> cycleService.isTxInCycle(cycle, proposal.getTxId()))
.collect(Collectors.toList());
List<EvaluatedProposal> evaluatedProposalsForCycle = voteResultService.getEvaluatedProposalList().stream()
List<EvaluatedProposal> evaluatedProposalsForCycle = bsqStateService.getEvaluatedProposalList().stream()
.filter(evaluatedProposal -> cycleService.isTxInCycle(cycle, evaluatedProposal.getProposal().getTxId()))
.collect(Collectors.toList());
List<DecryptedBallotsWithMerits> decryptedVotesForCycle = voteResultService.getDecryptedBallotsWithMeritsList().stream()
List<DecryptedBallotsWithMerits> decryptedVotesForCycle = bsqStateService.getDecryptedBallotsWithMeritsList().stream()
.filter(decryptedBallotsWithMerits -> cycleService.isTxInCycle(cycle, decryptedBallotsWithMerits.getBlindVoteTxId()))
.filter(decryptedBallotsWithMerits -> cycleService.isTxInCycle(cycle, decryptedBallotsWithMerits.getVoteRevealTxId()))
.collect(Collectors.toList());

View File

@ -44,8 +44,8 @@ import static com.google.common.base.Preconditions.checkArgument;
*/
@Deprecated
@Slf4j
public final class PersistableNetworkPayloadListService extends StoreService<PersistableNetworkPayloadList, PersistableNetworkPayload> {
public static final String FILE_NAME = "PersistableNetworkPayloadMap";
public final class PersistableNetworkPayloadListService extends MapStoreService<PersistableNetworkPayloadList, PersistableNetworkPayload> {
private static final String FILE_NAME = "PersistableNetworkPayloadMap";
///////////////////////////////////////////////////////////////////////////////////////////