Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hash of dao state #2532

Merged
merged 33 commits into from Mar 16, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0d0713b
Remove optional setting of pubKeyScript
ManfredKarrer Mar 8, 2019
22c0388
Add comment
ManfredKarrer Mar 8, 2019
2a4270d
Merge branch 'master' into add-hash-of-dao-state
ManfredKarrer Mar 8, 2019
40b6505
Add monitoring for hash of DaoState
ManfredKarrer Mar 9, 2019
51ec876
Add removeLinebreaks parameter to toTruncatedString
ManfredKarrer Mar 9, 2019
fcd7997
Create chain of dao state hashes for monitoring consensus issues
ManfredKarrer Mar 11, 2019
d09e770
Add parseBlockchainComplete check
ManfredKarrer Mar 11, 2019
e7bfba0
Add comment
ManfredKarrer Mar 11, 2019
bbec682
Add UI for Dao state monitor
ManfredKarrer Mar 11, 2019
f560413
Update UI, remove checkArgument call
ManfredKarrer Mar 12, 2019
dbb0ba3
Add check for address
ManfredKarrer Mar 12, 2019
c280f30
Use domain data or sorting
ManfredKarrer Mar 13, 2019
47024d1
Show dash instead of N/A for prev hash at genesis height
ManfredKarrer Mar 13, 2019
d235e9c
Avoid logging at startup
ManfredKarrer Mar 13, 2019
eb3d65d
Add snapshot support for hashChain
ManfredKarrer Mar 14, 2019
9c01560
Call listeners only after batch of hashes is processed
ManfredKarrer Mar 14, 2019
2009c30
Refactor dao state monitor
ManfredKarrer Mar 15, 2019
3685270
Move monitor view to new tab
ManfredKarrer Mar 15, 2019
59bed76
Add proposal monitor view
ManfredKarrer Mar 15, 2019
3411cde
Reflect code review comments
ManfredKarrer Mar 15, 2019
52e1455
Refactor updateHashChain
ManfredKarrer Mar 15, 2019
2109c66
Add num proposals
ManfredKarrer Mar 15, 2019
4af7495
Add blind vote monitoring
ManfredKarrer Mar 16, 2019
933fa46
Fix table layout issue
ManfredKarrer Mar 16, 2019
ccbb578
Add peer to log
ManfredKarrer Mar 16, 2019
00e350e
Small fixes
ManfredKarrer Mar 16, 2019
e3dace8
Fix tests
ManfredKarrer Mar 16, 2019
e933584
Remove db store file
ManfredKarrer Mar 16, 2019
0b15246
Add updated db files
ManfredKarrer Mar 16, 2019
b222fd2
Change file name for DaoStateStore db file to DaoStateStore2, Cleanup
ManfredKarrer Mar 16, 2019
82e2a48
Add comments
ManfredKarrer Mar 16, 2019
0ba2f6f
Cleanup after code inspection
ManfredKarrer Mar 16, 2019
b7941c8
Merge branch 'master' into add-hash-of-dao-state
ManfredKarrer Mar 16, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion common/src/main/java/bisq/common/app/Capability.java
Expand Up @@ -31,5 +31,6 @@ public enum Capability {
PROPOSAL,
BLIND_VOTE,
ACK_MSG,
BSQ_BLOCK
BSQ_BLOCK,
DAO_STATE
}
12 changes: 12 additions & 0 deletions common/src/main/proto/pb.proto
Expand Up @@ -57,6 +57,7 @@ message NetworkEnvelope {
AddPersistableNetworkPayloadMessage add_persistable_network_payload_message = 31;
AckMessage ack_message = 32;
RepublishGovernanceDataRequest republish_governance_data_request = 33;
NewDaoStateHashMessage new_dao_state_hash_message = 34;
}
}

Expand Down Expand Up @@ -324,6 +325,7 @@ message NewBlockBroadcastMessage {
message RepublishGovernanceDataRequest {
}


///////////////////////////////////////////////////////////////////////////////////////////
// Payload
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1715,6 +1717,16 @@ message DaoStateStore {
BsqState bsq_state = 1;
}

message DaoStateHash {
int32 blockHeight = 1;
bytes hash = 2;
bytes prev_hash = 3;
}

message NewDaoStateHashMessage {
DaoStateHash dao_state_hash = 1;
}

///////////////////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/bisq/core/dao/DaoModule.java
Expand Up @@ -78,6 +78,7 @@
import bisq.core.dao.state.DaoStateStorageService;
import bisq.core.dao.state.GenesisTxInfo;
import bisq.core.dao.state.model.DaoState;
import bisq.core.dao.state.monitoring.DaoStateMonitoringService;
import bisq.core.dao.state.unconfirmed.UnconfirmedBsqChangeOutputListService;

import bisq.common.app.AppModule;
Expand Down Expand Up @@ -116,6 +117,7 @@ protected void configure() {
bind(DaoStateService.class).in(Singleton.class);
bind(DaoStateSnapshotService.class).in(Singleton.class);
bind(DaoStateStorageService.class).in(Singleton.class);
bind(DaoStateMonitoringService.class).in(Singleton.class);
bind(UnconfirmedBsqChangeOutputListService.class).in(Singleton.class);

bind(ExportJsonFilesService.class).in(Singleton.class);
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/java/bisq/core/dao/DaoSetup.java
Expand Up @@ -35,6 +35,7 @@
import bisq.core.dao.node.BsqNodeProvider;
import bisq.core.dao.node.explorer.ExportJsonFilesService;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.monitoring.DaoStateMonitoringService;

import com.google.inject.Inject;

Expand Down Expand Up @@ -69,7 +70,8 @@ public DaoSetup(BsqNodeProvider bsqNodeProvider,
ProofOfBurnService proofOfBurnService,
DaoFacade daoFacade,
ExportJsonFilesService exportJsonFilesService,
DaoKillSwitch daoKillSwitch) {
DaoKillSwitch daoKillSwitch,
DaoStateMonitoringService daoStateMonitoringService) {

bsqNode = bsqNodeProvider.getBsqNode();

Expand All @@ -92,6 +94,8 @@ public DaoSetup(BsqNodeProvider bsqNodeProvider,
daoSetupServices.add(daoFacade);
daoSetupServices.add(exportJsonFilesService);
daoSetupServices.add(daoKillSwitch);
daoSetupServices.add(daoStateMonitoringService);

daoSetupServices.add(bsqNodeProvider.getBsqNode());
}

Expand Down
Expand Up @@ -79,7 +79,7 @@ public void start() {
public void onNewBlockHeight(int blockHeight) {
if (blockHeight != genesisBlockHeight)
maybeCreateNewCycle(blockHeight, daoStateService.getCycles())
.ifPresent(daoStateService.getCycles()::add);
.ifPresent(daoStateService::addCycle);
}


Expand All @@ -88,7 +88,7 @@ public void onNewBlockHeight(int blockHeight) {
///////////////////////////////////////////////////////////////////////////////////////////

public void addFirstCycle() {
daoStateService.getCycles().add(getFirstCycle());
daoStateService.addCycle(getFirstCycle());
}

public int getCycleIndex(Cycle cycle) {
Expand Down
Expand Up @@ -235,6 +235,7 @@ private void maybeCalculateVoteResult(int chainHeight) {
}

// Those which did not get accepted will be added to the nonBsq map
// FIXME add check for cycle as now we call addNonBsqTxOutput for past rejected comp requests as well
daoStateService.getIssuanceCandidateTxOutputs().stream()
.filter(txOutput -> !daoStateService.isIssuanceTx(txOutput.getTxId()))
.forEach(daoStateService::addNonBsqTxOutput);
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/bisq/core/dao/state/DaoStateListener.java
Expand Up @@ -32,4 +32,7 @@ default void onParseBlockComplete(Block block) {

default void onParseBlockCompleteAfterBatchProcessing(Block block) {
}

default void onDaoStateChanged(Block block) {
}
}
45 changes: 45 additions & 0 deletions core/src/main/java/bisq/core/dao/state/DaoStateService.java
Expand Up @@ -71,6 +71,7 @@ public class DaoStateService implements DaoSetupService {
private final List<DaoStateListener> daoStateListeners = new CopyOnWriteArrayList<>();
@Getter
private boolean parseBlockChainComplete;
private boolean allowDaoStateChange;


///////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -95,6 +96,8 @@ public void addListeners() {

@Override
public void start() {
allowDaoStateChange = true;
assertDaoStateChange();
daoState.setChainHeight(genesisTxInfo.getGenesisBlockHeight());
}

Expand All @@ -104,6 +107,9 @@ public void start() {
///////////////////////////////////////////////////////////////////////////////////////////

public void applySnapshot(DaoState snapshot) {
//TODO
assertDaoStateChange();

log.info("Apply snapshot with chain height {}", snapshot.getChainHeight());

daoState.setChainHeight(snapshot.getChainHeight());
Expand Down Expand Up @@ -144,6 +150,11 @@ DaoState getClone(DaoState snapshotCandidate) {
return DaoState.getClone(snapshotCandidate);
}

public byte[] getDaoStateHash() {
return daoState.toProtoMessage().toByteArray();
}



///////////////////////////////////////////////////////////////////////////////////////////
// ChainHeight
Expand All @@ -162,6 +173,11 @@ public LinkedList<Cycle> getCycles() {
return daoState.getCycles();
}

public void addCycle(Cycle firstCycle) {
ManfredKarrer marked this conversation as resolved.
Show resolved Hide resolved
assertDaoStateChange();
getCycles().add(firstCycle);
}

@Nullable
public Cycle getCurrentCycle() {
return !getCycles().isEmpty() ? getCycles().getLast() : null;
Expand Down Expand Up @@ -190,12 +206,14 @@ public Optional<Integer> getStartHeightOfNextCycle(int blockHeight) {

// First we get the blockHeight set
public void onNewBlockHeight(int blockHeight) {
allowDaoStateChange = true;
daoState.setChainHeight(blockHeight);
daoStateListeners.forEach(listener -> listener.onNewBlockHeight(blockHeight));
}

// Second we get the block added with empty txs
public void onNewBlockWithEmptyTxs(Block block) {
assertDaoStateChange();
if (daoState.getBlocks().isEmpty() && block.getHeight() != getGenesisBlockHeight()) {
log.warn("We don't have any blocks yet and we received a block which is not the genesis block. " +
"We ignore that block as the first block need to be the genesis block. " +
Expand All @@ -221,8 +239,14 @@ public void onParseBlockComplete(Block block) {
// We use 2 different handlers as we don't want to update domain listeners during batch processing of all
// blocks as that cause performance issues. In earlier versions when we updated at each block it took
// 50 sec. for 4000 blocks, after that change it was about 4 sec.
// Clients
if (parseBlockChainComplete)
daoStateListeners.forEach(l -> l.onParseBlockCompleteAfterBatchProcessing(block));

// Here listeners must not trigger any state change in the DAO as we trigger the validation service to
// generate a hash of the state.
allowDaoStateChange = false;
daoStateListeners.forEach(l -> l.onDaoStateChanged(block));
}

// Called after parsing of all pending blocks is completed
Expand Down Expand Up @@ -410,10 +434,12 @@ public Map<TxOutputKey, TxOutput> getUnspentTxOutputMap() {
}

public void addUnspentTxOutput(TxOutput txOutput) {
assertDaoStateChange();
getUnspentTxOutputMap().put(txOutput.getKey(), txOutput);
}

public void removeUnspentTxOutput(TxOutput txOutput) {
assertDaoStateChange();
getUnspentTxOutputMap().remove(txOutput.getKey());
}

Expand Down Expand Up @@ -547,6 +573,7 @@ public Set<TxOutput> getIssuanceCandidateTxOutputs() {
///////////////////////////////////////////////////////////////////////////////////////////

public void addIssuance(Issuance issuance) {
assertDaoStateChange();
daoState.getIssuanceMap().put(issuance.getTxId(), issuance);
}

Expand Down Expand Up @@ -595,7 +622,10 @@ public long getTotalIssuedAmount(IssuanceType issuanceType) {
// Non-BSQ
///////////////////////////////////////////////////////////////////////////////////////////

//TODO we never remove NonBsqTxOutput!
//FIXME called at result phase even if there is not a new one (passed txo from prev. cycle which was already added)
public void addNonBsqTxOutput(TxOutput txOutput) {
assertDaoStateChange();
checkArgument(txOutput.getTxOutputType() == TxOutputType.ISSUANCE_CANDIDATE_OUTPUT,
"txOutput must be type ISSUANCE_CANDIDATE_OUTPUT");
log.info("addNonBsqTxOutput: txOutput={}", txOutput);
Expand Down Expand Up @@ -827,6 +857,7 @@ public void confiscateBond(String lockupTxId) {
}

private void doConfiscateBond(String lockupTxId) {
assertDaoStateChange();
log.warn("TxId {} added to confiscatedLockupTxIdList.", lockupTxId);
daoState.getConfiscatedLockupTxList().add(lockupTxId);
}
Expand Down Expand Up @@ -855,6 +886,7 @@ public boolean isConfiscatedUnlockTxOutput(String unlockTxId) {
///////////////////////////////////////////////////////////////////////////////////////////

public void setNewParam(int blockHeight, Param param, String paramValue) {
assertDaoStateChange();
List<ParamChange> paramChangeList = daoState.getParamChangeList();
getStartHeightOfNextCycle(blockHeight)
.ifPresent(heightOfNewCycle -> {
Expand Down Expand Up @@ -903,6 +935,7 @@ public String getParamValue(Param param, int blockHeight) {
///////////////////////////////////////////////////////////////////////////////////////////

public void setSpentInfo(TxOutputKey txOutputKey, SpentInfo spentInfo) {
assertDaoStateChange();
daoState.getSpentInfoMap().put(txOutputKey, spentInfo);
}

Expand All @@ -920,6 +953,7 @@ public List<EvaluatedProposal> getEvaluatedProposalList() {
}

public void addEvaluatedProposalSet(Set<EvaluatedProposal> evaluatedProposals) {
assertDaoStateChange();
evaluatedProposals.stream()
.filter(e -> !daoState.getEvaluatedProposalList().contains(e))
.forEach(daoState.getEvaluatedProposalList()::add);
Expand All @@ -930,6 +964,7 @@ public List<DecryptedBallotsWithMerits> getDecryptedBallotsWithMeritsList() {
}

public void addDecryptedBallotsWithMeritsSet(Set<DecryptedBallotsWithMerits> decryptedBallotsWithMeritsSet) {
assertDaoStateChange();
decryptedBallotsWithMeritsSet.stream()
.filter(e -> !daoState.getDecryptedBallotsWithMeritsList().contains(e))
.forEach(daoState.getDecryptedBallotsWithMeritsList()::add);
Expand Down Expand Up @@ -964,5 +999,15 @@ public void addDaoStateListener(DaoStateListener listener) {
public void removeDaoStateListener(DaoStateListener listener) {
daoStateListeners.remove(listener);
}


///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////

private void assertDaoStateChange() {
if (!allowDaoStateChange)
throw new RuntimeException("We got a call which would change the daoState outside of the allowed event phase");
}
}

@@ -0,0 +1,71 @@
/*
* 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.monitoring;

import bisq.common.proto.network.NetworkPayload;
import bisq.common.proto.persistable.PersistablePayload;
import bisq.common.util.Utilities;

import io.bisq.generated.protobuffer.PB;

import com.google.protobuf.ByteString;

import lombok.Value;

@Value
public class DaoStateHash implements PersistablePayload, NetworkPayload {
private final int blockHeight;
private final byte[] hash;
// For first block the prevHash is an empty byte array
private final byte[] prevHash;

DaoStateHash(int blockHeight, byte[] hash, byte[] prevHash) {
this.blockHeight = blockHeight;
this.hash = hash;
this.prevHash = prevHash;
}


///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////

@Override
public PB.DaoStateHash toProtoMessage() {
final PB.DaoStateHash.Builder builder = PB.DaoStateHash.newBuilder()
.setBlockHeight(blockHeight)
.setHash(ByteString.copyFrom(hash))
.setPrevHash(ByteString.copyFrom(prevHash));
return builder.build();
}

public static DaoStateHash fromProto(PB.DaoStateHash proto) {
return new DaoStateHash(proto.getBlockHeight(),
proto.getHash().toByteArray(),
proto.getPrevHash().toByteArray());
}

@Override
public String toString() {
return "DaoStateHash{" +
"\n blockHeight=" + blockHeight +
",\n hash=" + Utilities.bytesAsHexString(hash) +
",\n prevHash=" + Utilities.bytesAsHexString(prevHash) +
"\n}";
}
}
@@ -0,0 +1,22 @@
/*
* 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.monitoring;

public interface DaoStateMonitorListener {
void onDaoStateNetworkConsensusChanged();
}