feat(engine-byzantine): add crate for simulating Byzantine faults in consensus #1505
Open
hvanz wants to merge 6 commits intocirclefin:mainfrom
Open
feat(engine-byzantine): add crate for simulating Byzantine faults in consensus #1505hvanz wants to merge 6 commits intocirclefin:mainfrom
hvanz wants to merge 6 commits intocirclefin:mainfrom
Conversation
Introduces a network proxy actor that intercepts outgoing consensus messages to equivocate or drop votes/proposals, and a middleware that ignores voting locks (amnesia attack). Attacks are configurable via TOML triggers (always, random, at specific heights/rounds, or ranges).
Integrate the engine-byzantine crate into the test app's node startup to conditionally inject the network proxy and amnesia middleware based on the new [byzantine] config section.
Previously proposal equivocation only logged a warning and forwarded the original. Now it constructs and sends a conflicting proposal via an optional ConflictingValueFn callback. The test app provides a factory that creates a conflicting value by incrementing the original's u64.
…narios Cover vote/proposal equivocation detection, message dropping, and amnesia attacks using the Byzantine engine with the test framework.
The ByzantineNetworkProxy only intercepted votes on the consensus gossip channel but forwarded them transparently on the liveness (rebroadcast) channel. This meant drop_votes was ineffective — votes still reached peers through the liveness path. Fix the proxy to also apply drop_votes rules to PublishLivenessMsg containing votes. Add an integration test (two_byzantine_of_four_stalls_consensus) that verifies 2 Byzantine vote-droppers out of 4 equal-power nodes cause consensus to stall, confirming the f < n/3 fault threshold. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add four tests covering different attack types with 2 out of 4 equal-power Byzantine nodes: - two_silent_of_four_stalls_consensus: total silence (drop votes + proposals) causes consensus to stall - two_proposal_droppers_of_four_still_progresses: dropping proposals alone does not prevent liveness since honest proposer rounds still form quorum - two_vote_equivocators_of_four_detected: equivocation is detected via MisbehaviorEvidence while consensus still progresses - two_amnesia_of_four_still_progresses: ignoring locks alone does not prevent liveness since all nodes still vote Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This comment was marked as resolved.
This comment was marked as resolved.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes: #1504
This PR introduces a new
engine-byzantinecrate that provides configurable Byzantine behavior simulation, along with a comprehensive integration test suite that exercises vote equivocation, proposal equivocation, message dropping, amnesia attacks, and consensus stall scenarios.The new crate provides two main components that compose with the existing engine architecture:
ByzantineNetworkProxy: A ractor actor that sits between the consensus actor and the real network actor. It intercepts outgoingPublishConsensusMsgandPublishLivenessMsgmessages and can drop them (simulating silence/censorship), duplicate them with conflicting content (simulating equivocation), or forward them unchanged. All other message types pass through transparently.ByzantineMiddleware: Wraps the test app'sMiddlewaretrait to override vote construction. Whenignore_locksis enabled, it tracks proposed values and overrides nil prevotes to vote for the most recently proposed value, simulating an amnesia attack (ignoring the Tendermint voting lock mechanism).Both components are driven by a
ByzantineConfigstruct that supports five attack types:equivocate_votesequivocate_proposalsdrop_votesdrop_proposalsignore_locksEach attack is gated by a
Triggerthat controls when it fires:Always,Random { probability },AtHeights,AtRounds, orHeightRange.Also, integrated the Byzantine engine with test app (
crates/test/app/):ByzantineConfigas an optional field on the test app'sConfigstruct.App::start()to conditionally spawn aByzantineNetworkProxyin front of the real network actor and wrap the middleware withByzantineMiddlewarewhen amnesia is configured.Testing
The following integration tests where added to
crates/test/tests/it/byzantine_engine.rs:1-of-4 Byzantine node scenarios (minority fault):
vote_equivocation_detected— Verifies honest nodes collectMisbehaviorEvidencefor vote equivocation.proposal_equivocation_detected— (Currently#[ignore]) Verifies proposal equivocation detection. Known limitation: gossip latency causes conflicting proposals to arrive after height decision.proposal_dropping_liveness— Confirms the network progresses when one node drops all proposals.vote_dropping_liveness— Confirms the network progresses when one node drops all votes.amnesia_attack_liveness— Confirms the network progresses despite one node ignoring voting locks.2-of-4 Byzantine node scenarios (exceeding fault threshold):
two_byzantine_of_four_stalls_consensus— Verifies that when 2 of 4 equal-power nodes drop all votes (50% of voting power), consensus correctly stalls (no decisions made), confirming the f < n/3 fault threshold.two_silent_of_four_stalls_consensus— Same as above but with both votes and proposals dropped.two_proposal_droppers_of_four_still_progresses— Verifies that dropping proposals alone (while still voting) does not prevent liveness, since honest proposer rounds still succeed.two_vote_equivocators_of_four_detected— Verifies equivocation evidence is collected even with 2 equivocating nodes.two_amnesia_of_four_still_progresses— Verifies that amnesia alone does not break liveness when all votes are still delivered.PR author checklist
Contribution eligibility
For all contributors
RELEASE_NOTES.mdif the change warrants itBREAKING_CHANGES.mdif the change warrants itFor external contributors