Search in sources :

Example 1 with PreparedVertex

use of com.radixdlt.hotstuff.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);
}
Also used : PreparedVertex(com.radixdlt.hotstuff.bft.PreparedVertex) DeterministicTest(com.radixdlt.harness.deterministic.DeterministicTest) Provides(com.google.inject.Provides) BFTValidatorSet(com.radixdlt.hotstuff.bft.BFTValidatorSet) ProvidesIntoSet(com.google.inject.multibindings.ProvidesIntoSet) AbstractModule(com.google.inject.AbstractModule) EventProcessor(com.radixdlt.environment.EventProcessor) ProcessOnDispatch(com.radixdlt.environment.ProcessOnDispatch) HashMap(java.util.HashMap) Map(java.util.Map) ProposerElection(com.radixdlt.hotstuff.liveness.ProposerElection) Test(org.junit.Test) DeterministicTest(com.radixdlt.harness.deterministic.DeterministicTest)

Example 2 with PreparedVertex

use of com.radixdlt.hotstuff.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);
}
Also used : HighQC(com.radixdlt.hotstuff.HighQC) BFTHeader(com.radixdlt.hotstuff.BFTHeader) BFTNode(com.radixdlt.hotstuff.bft.BFTNode) Vote(com.radixdlt.hotstuff.Vote) PreparedVertex(com.radixdlt.hotstuff.bft.PreparedVertex) QuorumCertificate(com.radixdlt.hotstuff.QuorumCertificate) View(com.radixdlt.hotstuff.bft.View) BFTInsertUpdate(com.radixdlt.hotstuff.bft.BFTInsertUpdate) ViewUpdate(com.radixdlt.hotstuff.bft.ViewUpdate) VerifiedVertex(com.radixdlt.hotstuff.bft.VerifiedVertex) LedgerHeader(com.radixdlt.hotstuff.LedgerHeader) HashCode(com.google.common.hash.HashCode) VerifiedVertexStoreState(com.radixdlt.hotstuff.bft.VerifiedVertexStoreState) Test(org.junit.Test)

Example 3 with PreparedVertex

use of com.radixdlt.hotstuff.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()));
}
Also used : VerifiedVertex(com.radixdlt.hotstuff.bft.VerifiedVertex) PreparedVertex(com.radixdlt.hotstuff.bft.PreparedVertex) StateComputerResult(com.radixdlt.ledger.StateComputerLedger.StateComputerResult) Test(org.junit.Test)

Example 4 with PreparedVertex

use of com.radixdlt.hotstuff.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());
}
Also used : HighQC(com.radixdlt.hotstuff.HighQC) VerifiedVertex(com.radixdlt.hotstuff.bft.VerifiedVertex) PreparedVertex(com.radixdlt.hotstuff.bft.PreparedVertex) QuorumCertificate(com.radixdlt.hotstuff.QuorumCertificate) Txn(com.radixdlt.atom.Txn) UnverifiedVertex(com.radixdlt.hotstuff.UnverifiedVertex)

Example 5 with PreparedVertex

use of com.radixdlt.hotstuff.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()));
    }
}
Also used : LedgerHeader(com.radixdlt.hotstuff.LedgerHeader) PreparedVertex(com.radixdlt.hotstuff.bft.PreparedVertex)

Aggregations

PreparedVertex (com.radixdlt.hotstuff.bft.PreparedVertex)9 VerifiedVertex (com.radixdlt.hotstuff.bft.VerifiedVertex)6 Test (org.junit.Test)5 LedgerHeader (com.radixdlt.hotstuff.LedgerHeader)4 Txn (com.radixdlt.atom.Txn)3 QuorumCertificate (com.radixdlt.hotstuff.QuorumCertificate)3 StateComputerResult (com.radixdlt.ledger.StateComputerLedger.StateComputerResult)3 Provides (com.google.inject.Provides)2 HighQC (com.radixdlt.hotstuff.HighQC)2 UnverifiedVertex (com.radixdlt.hotstuff.UnverifiedVertex)2 BFTNode (com.radixdlt.hotstuff.bft.BFTNode)2 BFTValidatorSet (com.radixdlt.hotstuff.bft.BFTValidatorSet)2 View (com.radixdlt.hotstuff.bft.View)2 LinkedList (java.util.LinkedList)2 ImmutableList (com.google.common.collect.ImmutableList)1 ImmutableMap (com.google.common.collect.ImmutableMap)1 HashCode (com.google.common.hash.HashCode)1 AbstractModule (com.google.inject.AbstractModule)1 Singleton (com.google.inject.Singleton)1 ProvidesIntoSet (com.google.inject.multibindings.ProvidesIntoSet)1