use of com.radixdlt.consensus.bft.PreparedVertex in project radixdlt by radixdlt.
the class PacemakerTest method when_local_timeout__then_send_empty_vote_if_no_previous.
@Test
public void when_local_timeout__then_send_empty_vote_if_no_previous() {
HighQC viewUpdateHighQc = mock(HighQC.class);
QuorumCertificate committedQc = mock(QuorumCertificate.class);
QuorumCertificate highestQc = mock(QuorumCertificate.class);
when(viewUpdateHighQc.highestCommittedQC()).thenReturn(committedQc);
when(viewUpdateHighQc.highestQC()).thenReturn(highestQc);
BFTHeader highestQcProposed = mock(BFTHeader.class);
HashCode highQcParentVertexId = mock(HashCode.class);
when(highestQcProposed.getVertexId()).thenReturn(highQcParentVertexId);
when(highestQc.getProposed()).thenReturn(highestQcProposed);
when(committedQc.getView()).thenReturn(View.of(0));
ViewUpdate viewUpdate = ViewUpdate.create(View.of(1), viewUpdateHighQc, mock(BFTNode.class), mock(BFTNode.class));
this.pacemaker.processViewUpdate(viewUpdate);
View view = View.of(1);
Vote emptyVote = mock(Vote.class);
Vote emptyVoteWithTimeout = mock(Vote.class);
ImmutableSet<BFTNode> validators = rmock(ImmutableSet.class);
BFTHeader bftHeader = mock(BFTHeader.class);
HighQC highQC = mock(HighQC.class);
BFTInsertUpdate bftInsertUpdate = mock(BFTInsertUpdate.class);
when(bftInsertUpdate.getHeader()).thenReturn(bftHeader);
PreparedVertex preparedVertex = mock(PreparedVertex.class);
when(preparedVertex.getView()).thenReturn(view);
when(preparedVertex.getLedgerHeader()).thenReturn(mock(LedgerHeader.class));
VerifiedVertexStoreState vertexStoreState = mock(VerifiedVertexStoreState.class);
when(vertexStoreState.getHighQC()).thenReturn(highQC);
when(bftInsertUpdate.getInserted()).thenReturn(preparedVertex);
when(bftInsertUpdate.getVertexStoreState()).thenReturn(vertexStoreState);
var node = BFTNode.random();
when(preparedVertex.getId()).thenReturn(hasher.hash(UnverifiedVertex.createTimeout(highestQc, view, node)));
when(this.safetyRules.getLastVote(view)).thenReturn(Optional.empty());
when(this.safetyRules.createVote(any(), any(), anyLong(), any())).thenReturn(emptyVote);
when(this.safetyRules.timeoutVote(emptyVote)).thenReturn(emptyVoteWithTimeout);
when(this.validatorSet.nodes()).thenReturn(validators);
when(this.vertexStore.getPreparedVertex(any())).thenReturn(Optional.empty());
this.pacemaker.processLocalTimeout(ScheduledLocalTimeout.create(ViewUpdate.create(View.of(1), mock(HighQC.class), node, BFTNode.random()), 0L));
this.pacemaker.processBFTUpdate(bftInsertUpdate);
verify(this.voteDispatcher, times(1)).dispatch(eq(validators), eq(emptyVoteWithTimeout));
verify(this.safetyRules, times(1)).getLastVote(view);
verify(this.safetyRules, times(1)).createVote(any(), any(), anyLong(), any());
verify(this.safetyRules, times(1)).timeoutVote(emptyVote);
verifyNoMoreInteractions(this.safetyRules);
verify(this.vertexStore, times(1)).getPreparedVertex(any());
ArgumentCaptor<VerifiedVertex> insertVertexCaptor = ArgumentCaptor.forClass(VerifiedVertex.class);
verify(this.vertexStore, times(1)).insertVertex(insertVertexCaptor.capture());
assertEquals(insertVertexCaptor.getValue().getParentId(), highQcParentVertexId);
verifyNoMoreInteractions(this.vertexStore);
}
use of com.radixdlt.consensus.bft.PreparedVertex in project radixdlt by radixdlt.
the class StateComputerLedgerTest method should_not_change_header_when_past_end_of_epoch_even_with_command.
@Test
public void should_not_change_header_when_past_end_of_epoch_even_with_command() {
// Arrange
genesisIsEndOfEpoch(true);
when(stateComputer.prepare(any(), any(), anyLong())).thenReturn(new StateComputerResult(ImmutableList.of(successfulNextCommand), ImmutableMap.of()));
var unverifiedVertex = UnverifiedVertex.create(genesisQC, View.of(1), List.of(nextTxn), BFTNode.random());
var proposedVertex = new VerifiedVertex(unverifiedVertex, hasher.hash(unverifiedVertex));
// Act
Optional<PreparedVertex> nextPrepared = sut.prepare(new LinkedList<>(), proposedVertex);
// Assert
assertThat(nextPrepared).hasValueSatisfying(x -> assertThat(x.getLedgerHeader().isEndOfEpoch()).isTrue());
assertThat(nextPrepared).hasValueSatisfying(x -> assertThat(x.getLedgerHeader().getAccumulatorState()).isEqualTo(ledgerHeader.getAccumulatorState()));
}
use of com.radixdlt.consensus.bft.PreparedVertex in project radixdlt by radixdlt.
the class PacemakerViewUpdateRaceConditionTest method test_pacemaker_view_update_race_condition.
@Test
public void test_pacemaker_view_update_race_condition() {
final DeterministicTest test = DeterministicTest.builder().numNodes(numNodes).messageSelector(MessageSelector.randomSelector(random)).messageMutator(messUpMessagesForNodeUnderTest()).pacemakerTimeout(pacemakerTimeout).overrideWithIncorrectModule(new AbstractModule() {
@ProvidesIntoSet
@ProcessOnDispatch
private EventProcessor<BFTInsertUpdate> bftInsertUpdateProcessor() {
final Map<HashCode, PreparedVertex> insertedVertices = new HashMap<>();
return bftInsertUpdate -> {
final PreparedVertex inserted = bftInsertUpdate.getInserted();
insertedVertices.putIfAbsent(inserted.getId(), inserted);
final Optional<PreparedVertex> maybeParent = Optional.ofNullable(insertedVertices.get(inserted.getParentId()));
maybeParent.ifPresent(parent -> {
if (parent.getView().equals(inserted.getView())) {
throw new IllegalStateException("Vertex can't have the same view as its parent.");
}
});
};
}
@Provides
public ProposerElection proposerElection(BFTValidatorSet validatorSet) {
final var sortedValidators = validatorSet.getValidators().stream().map(BFTValidator::getNode).sorted(Comparator.comparing(BFTNode::getKey, KeyComparator.instance().reversed())).toList();
return view -> sortedValidators.get(((int) view.number() - 1) % sortedValidators.size());
}
}).buildWithoutEpochs().runUntil(nodeUnderTestReachesView(View.of(3)));
final var counters = test.getSystemCounters(nodeUnderTestIndex);
assertThat(counters.get(SystemCounters.CounterType.BFT_VOTE_QUORUMS)).isEqualTo(// ensure that quorum was formed
2);
assertThat(counters.get(SystemCounters.CounterType.BFT_PACEMAKER_TIMEOUTS_SENT)).isEqualTo(// ensure that timeouts were processed
2);
}
use of com.radixdlt.consensus.bft.PreparedVertex in project radixdlt by radixdlt.
the class Pacemaker method generateProposal.
private Optional<Proposal> generateProposal(View view) {
final HighQC highQC = this.latestViewUpdate.getHighQC();
final QuorumCertificate highestQC = highQC.highestQC();
final List<Txn> nextTxns;
// TODO: Remove isEndOfEpoch knowledge from consensus
if (highestQC.getProposed().getLedgerHeader().isEndOfEpoch()) {
nextTxns = List.of();
} else {
final List<PreparedVertex> preparedVertices = vertexStore.getPathFromRoot(highestQC.getProposed().getVertexId());
nextTxns = nextTxnsGenerator.generateNextTxns(view, preparedVertices);
systemCounters.add(SystemCounters.CounterType.BFT_PACEMAKER_PROPOSED_TRANSACTIONS, nextTxns.size());
}
final UnverifiedVertex proposedVertex = UnverifiedVertex.create(highestQC, view, nextTxns, self);
final VerifiedVertex verifiedVertex = new VerifiedVertex(proposedVertex, hasher.hash(proposedVertex));
return safetyRules.signProposal(verifiedVertex, highQC.highestCommittedQC(), highQC.highestTC());
}
use of com.radixdlt.consensus.bft.PreparedVertex in project radixdlt by radixdlt.
the class StateComputerLedger method prepare.
@Override
public Optional<PreparedVertex> prepare(LinkedList<PreparedVertex> previous, VerifiedVertex vertex) {
final LedgerHeader parentHeader = vertex.getParentHeader().getLedgerHeader();
final AccumulatorState parentAccumulatorState = parentHeader.getAccumulatorState();
final ImmutableList<PreparedTxn> prevCommands = previous.stream().flatMap(PreparedVertex::successfulCommands).collect(ImmutableList.toImmutableList());
final long quorumTimestamp;
// one view per epoch but good enough for now
if (vertex.getParentHeader().getView().isGenesis()) {
quorumTimestamp = vertex.getParentHeader().getLedgerHeader().timestamp();
} else {
quorumTimestamp = vertex.getQC().getTimestampedSignatures().weightedTimestamp();
}
synchronized (lock) {
if (this.currentLedgerHeader.getStateVersion() > parentAccumulatorState.getStateVersion()) {
return Optional.empty();
}
// Don't execute atom if in process of epoch change
if (parentHeader.isEndOfEpoch()) {
final long localTimestamp = timeSupplier.currentTime();
final PreparedVertex preparedVertex = vertex.withHeader(parentHeader.updateViewAndTimestamp(vertex.getView(), quorumTimestamp), localTimestamp).andTxns(ImmutableList.of(), ImmutableMap.of());
return Optional.of(preparedVertex);
}
final var maybeCommands = this.verifier.verifyAndGetExtension(this.currentLedgerHeader.getAccumulatorState(), prevCommands, p -> p.txn().getId().asHashCode(), parentAccumulatorState);
// Can possibly get here without maliciousness if parent vertex isn't locked by everyone else
if (maybeCommands.isEmpty()) {
return Optional.empty();
}
final var concatenatedCommands = maybeCommands.get();
final StateComputerResult result = stateComputer.prepare(concatenatedCommands, vertex, quorumTimestamp);
AccumulatorState accumulatorState = parentHeader.getAccumulatorState();
for (PreparedTxn txn : result.getSuccessfulCommands()) {
accumulatorState = this.accumulator.accumulate(accumulatorState, txn.txn().getId().asHashCode());
}
final LedgerHeader ledgerHeader = LedgerHeader.create(parentHeader.getEpoch(), vertex.getView(), accumulatorState, quorumTimestamp, result.getNextValidatorSet().orElse(null));
final long localTimestamp = timeSupplier.currentTime();
return Optional.of(vertex.withHeader(ledgerHeader, localTimestamp).andTxns(result.getSuccessfulCommands(), result.getFailedCommands()));
}
}
Aggregations