Fix handling of reorgs

- apply snapshot in case of a reorg and if the snapshot is still empty.
Set chain height to genesis height.
- Handle case if cycles are empty
- Add checks for getLast for linked lists
This commit is contained in:
Manfred Karrer 2018-12-02 22:31:38 +01:00
parent e1e9180291
commit d4729751e8
No known key found for this signature in database
GPG Key ID: 401250966A6B2C46
7 changed files with 25 additions and 17 deletions

View File

@ -122,6 +122,7 @@ public class MyVoteListService implements PersistedDataHost {
public List<MyVote> getMyVoteListForCycle() {
return myVoteList.getList().stream()
.filter(e -> daoStateService.getCurrentCycle() != null)
.filter(e -> daoStateService.getCurrentCycle().isInCycle(e.getHeight()))
.collect(Collectors.toList());
}

View File

@ -105,9 +105,9 @@ public class CycleService implements DaoStateListener, DaoSetupService {
// applied the new cycle yet. But the first block of the old cycle will always be the same as the
// first block of the new cycle.
Cycle cycle = null;
if (blockHeight != genesisBlockHeight && isFirstBlockAfterPreviousCycle(blockHeight, cycles)) {
if (blockHeight != genesisBlockHeight && isFirstBlockAfterPreviousCycle(blockHeight, cycles) && !cycles.isEmpty()) {
// We have the not update daoStateService.getCurrentCycle() so we grab here the previousCycle
final Cycle previousCycle = cycles.getLast();
Cycle previousCycle = cycles.getLast();
// We create the new cycle as clone of the previous cycle and only if there have been change events we use
// the new values from the change event.
cycle = createNewCycle(blockHeight, previousCycle);

View File

@ -29,6 +29,8 @@ import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
@Slf4j
public final class PeriodService {
private final DaoStateService daoStateService;
@ -52,6 +54,7 @@ public final class PeriodService {
return daoStateService.getCycles();
}
@Nullable
public Cycle getCurrentCycle() {
return daoStateService.getCurrentCycle();
}
@ -65,7 +68,9 @@ public final class PeriodService {
}
public DaoPhase.Phase getCurrentPhase() {
return getCurrentCycle().getPhaseForHeight(this.getChainHeight()).get();
return getCurrentCycle() != null ?
getCurrentCycle().getPhaseForHeight(this.getChainHeight()).orElse(DaoPhase.Phase.UNDEFINED) :
DaoPhase.Phase.UNDEFINED;
}
public boolean isFirstBlockInCycle(int height) {

View File

@ -137,7 +137,7 @@ public abstract class BsqNode implements DaoSetupService {
@SuppressWarnings("WeakerAccess")
protected void onInitialized() {
applySnapshot();
daoStateSnapshotService.applySnapshot(false);
if (p2PService.isBootstrapped()) {
log.info("onAllServicesInitialized: isBootstrapped");
@ -187,20 +187,11 @@ public abstract class BsqNode implements DaoSetupService {
@SuppressWarnings("WeakerAccess")
protected void startReOrgFromLastSnapshot() {
applySnapshot();
daoStateSnapshotService.applySnapshot(true);
startParseBlocks();
}
protected boolean isBlockAlreadyAdded(RawBlock rawBlock) {
return daoStateService.getBlockAtHeight(rawBlock.getHeight()).isPresent();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void applySnapshot() {
daoStateSnapshotService.applySnapshot();
}
}

View File

@ -121,7 +121,7 @@ public class BlockParser {
private void validateIfBlockIsConnecting(RawBlock rawBlock) throws BlockNotConnectingException {
LinkedList<Block> blocks = daoStateService.getBlocks();
if (!isBlockConnecting(rawBlock, blocks)) {
if (!isBlockConnecting(rawBlock, blocks) && !blocks.isEmpty()) {
Block last = blocks.getLast();
log.warn("addBlock called with a not connecting block. New block:\n" +
"height()={}, hash()={}, lastBlock.height()={}, lastBlock.hash()={}",

View File

@ -55,6 +55,8 @@ import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
/**
@ -99,6 +101,8 @@ public class DaoStateService implements DaoSetupService {
///////////////////////////////////////////////////////////////////////////////////////////
public void applySnapshot(DaoState snapshot) {
log.info("Apply snapshot with chain height {}", snapshot.getChainHeight());
daoState.setChainHeight(snapshot.getChainHeight());
daoState.getBlocks().clear();
@ -155,8 +159,9 @@ public class DaoStateService implements DaoSetupService {
return daoState.getCycles();
}
@Nullable
public Cycle getCurrentCycle() {
return getCycles().getLast();
return !getCycles().isEmpty() ? getCycles().getLast() : null;
}
public Optional<Cycle> getCycle(int height) {

View File

@ -77,6 +77,7 @@ public class DaoStateSnapshotService implements DaoStateListener {
// different to our current height.
boolean noSnapshotCandidateOrDifferentHeight = snapshotCandidate == null || snapshotCandidate.getChainHeight() != chainHeight;
if (isSnapshotHeight(chainHeight) &&
!daoStateService.getBlocks().isEmpty() &&
isValidHeight(daoStateService.getBlocks().getLast().getHeight()) &&
noSnapshotCandidateOrDifferentHeight) {
// At trigger event we store the latest snapshotCandidate to disc
@ -108,7 +109,7 @@ public class DaoStateSnapshotService implements DaoStateListener {
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void applySnapshot() {
public void applySnapshot(boolean fromReorg) {
DaoState persisted = daoStateStorageService.getPersistedBsqState();
if (persisted != null) {
LinkedList<Block> blocks = persisted.getBlocks();
@ -117,6 +118,11 @@ public class DaoStateSnapshotService implements DaoStateListener {
log.info("applySnapshot from persisted daoState with height of last block {}", heightOfLastBlock);
if (isValidHeight(heightOfLastBlock))
daoStateService.applySnapshot(persisted);
} else if (fromReorg) {
log.info("We got a reorg and we want to apply the snapshot but it is empty. That is expected in the first blocks until the " +
"first snapshot has been created. We use our applySnapshot method and restart from the genesis tx");
persisted.setChainHeight(genesisTxInfo.getGenesisBlockHeight());
daoStateService.applySnapshot(persisted);
}
} else {
log.info("Try to apply snapshot but no stored snapshot available. That is expected at first blocks.");