Skip to content

docs: Hashgraph Invariants#25966

Open
netopyr wants to merge 8 commits into
mainfrom
25352-hashgraph-invariants
Open

docs: Hashgraph Invariants#25966
netopyr wants to merge 8 commits into
mainfrom
25352-hashgraph-invariants

Conversation

@netopyr

@netopyr netopyr commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Description:

This PR adds several invariants from the Hashgraph algorithm and fixes some links and minor formatting issues.

Related issue(s):

Fixes #25352

netopyr added 8 commits June 8, 2026 15:17
Signed-off-by: Michael Heinrichs <netopyr@users.noreply.github.com>
Signed-off-by: Michael Heinrichs <netopyr@users.noreply.github.com>
Signed-off-by: Michael Heinrichs <netopyr@users.noreply.github.com>
Signed-off-by: Michael Heinrichs <netopyr@users.noreply.github.com>
Signed-off-by: Michael Heinrichs <netopyr@users.noreply.github.com>
# Conflicts:
#	platform-sdk/docs/consensus-layer/invariants/INV-001-roundcreated-monotonic-along-ancestry.md
Signed-off-by: Michael Heinrichs <netopyr@users.noreply.github.com>
@netopyr netopyr added this to the v0.77 milestone Jun 17, 2026
@netopyr netopyr self-assigned this Jun 17, 2026
@netopyr netopyr requested a review from a team as a code owner June 17, 2026 12:09
@netopyr netopyr requested a review from abies June 17, 2026 12:09
@trunk-io

trunk-io Bot commented Jun 17, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@lfdt-bot

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@poulok poulok self-requested a review June 22, 2026 13:37
---
type: invariant
id: INV-017
title: The active address book never moves backward — roster and stake changes apply forward-only

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this invariant makes a lot of sense. We can have address book A, then address book B, then address book C, which is exactly the same as address book A. At that point, what is a real difference between saying that we have moved forward to address book C or reused address book A?

I would probably state something about each round having well defined address book, I don't think that saying that "sequence of address books is monotonic" makes mathematical sense.

@poulok poulok left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got to all the files up through INV-004. I'll pickup with INV-005 in the next round. Looking good so far!


## Statement

If an honest event becomes ancient before reaching consensus, every honest node agrees it is stale: it is assigned no consensus order or timestamp on any node. No node orders an event that another node treats as stale.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the statement needs to be a bit more specific. Not all honest nodes are guaranteed to ever know about a stale event, and a node cannot agree on something it does not know about. The stale-events.md concept document says this, but it should be said here also in the statement:

Because a stale event is by definition an event that does not reach
consensus and never will, stale event reports are **not deterministic
across nodes**.

I suggest:

If an honest event becomes ancient before reaching consensus, every honest node that knows of the event agrees it is stale: it is assigned no consensus order or timestamp on any node. No node orders an event that another node treats as stale.


## Change risk

- **A node-local ancient boundary** — deriving the boundary from per-node state so two nodes age out different events.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see this statement being a little confusing. The non ancient boundary is calculated locally albeit from the state that every node is guaranteed to reach deterministically. This is even stated above: The birth round is immutable and gossiped, and the boundary minNonAncientRound is a function of the agreed round state;. The key is that the determination must never be based on a non-deterministic per-node state.

- **Ordering an event past the boundary on one node** while another has already discarded it as stale.
- **Mutating an event's birth round** after creation, so the staleness test gives different answers on different nodes.

Any change that lets one correct node order an event another has declared stale forks the ordered stream.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add something about causing an ISS here so that it links to any scenarios we add later?

- [Reconnect](../topics/reconnect.md) — `StateLifecycleManager.createStateFrom` / `initWithState`
and `onStateInitialized` with `InitTrigger.RECONNECT`; resets the pre-handle contract baseline.
- Invariants / Decisions: _(TBD: catalogs not yet populated)_.
- Invariants: INV-005 (every honest event eventually reaches consensus or becomes stale), INV-004 (staleness is agreed by all nodes) — see the [pre-handle → consensus-or-stale contract](#every-pre-handled-event-reaches-consensus-or-goes-stale) above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to my comment on INV-004, the statement "staleness is agreed by all nodes" could be a little misleading.

- Topics: [wiring-framework.md](wiring-framework.md), [event-creator.md](event-creator.md), [gossip.md](gossip.md), [restart-and-pces.md](restart-and-pces.md), [reasons-not-to-gossip.md](reasons-not-to-gossip.md).
- Tunables: [../../tunables.md](../../tunables.md).
- Invariants: [TBD: INV-NNN once invariants.md catalog populates].
- Invariants: INV-005 — every honest event eventually reaches consensus or becomes stale (what the throttling reactions ultimately protect).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this invariant have to do with throttling?

[`branching.md`](branching.md),
[`strongly-seeing.md`](strongly-seeing.md),
[`coin-rounds.md`](coin-rounds.md).
- Invariants: INV-006 (every round eventually has at least one judge), INV-007 (all deciders of a round agree on its judge set), INV-014 (every round has a supermajority of judges).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it seems that INV-014 supercedes INV-006 🤔

[`../architecture/topics/event-intake.md`](../architecture/topics/event-intake.md),
[`../architecture/topics/gossip.md`](../architecture/topics/gossip.md).
- Sibling concept: [`event-lifecycle.md`](event-lifecycle.md).
- Invariants: INV-004 (staleness is agreed by all nodes — the *fate*, not the local stale-report stream this concept calls non-deterministic), INV-005 (every honest event eventually reaches consensus or becomes stale).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh the clarification here about the stale "fate" of an event, not the reporting of the stale event, helps a lot. I think this clarification should be added to the invariant wording and possibly the summary used when referencing the invariant.

type: invariant
id: INV-001
title: roundCreated is monotonic along ancestry — a parent's round never exceeds its child's
title: Voting round is monotonic along ancestry — a parent's round never exceeds its child's

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filename should also be updated to use the term "voting round"

---
type: invariant
id: INV-002
title: Consensus order is agreed by all nodes — any two nodes that order an event report the same order

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any two honest nodes


The invariant is at risk whenever consensus order is made to depend on something not shared identically across nodes. Mechanisms to defend against:

- **Node-local state in the ordering computation** — breaking ties in `before` with a value that differs between nodes (wall-clock arrival time, local receive sequence, peer identity). The tie-break must be a deterministic function of the events alone.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **Node-local state in the ordering computation** — breaking ties in `before` with a value that differs between nodes (wall-clock arrival time, local receive sequence, peer identity). The tie-break must be a deterministic function of the events alone.
- **Node-local state in the ordering computation** — breaking ties in `before` with a value that differs between nodes (wall-clock arrival time, local receive sequence, peer identity). The tie-break must be a deterministic function of the immutable event properties alone.

@poulok poulok left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed these files do not wrap long lines. Spotless passed, but still there are very long single lines. I'm not sure what happened but it would be nice to not have an entire paragraph on a single line for review purposes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we agreed to use the term "consensus" instead of "decided" for consistency.


## Statement

Under the algorithm's standard assumptions, every event created by an honest node eventually reaches a final fate — a consensus order and timestamp, or staleness — with probability 1. No honest event remains forever undecided.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this also true of dishonest events? (btw, it's a little strange to think of honest and dishonest events rather than nodes). Maybe not if we define these fates as only applying to events that at in the hashgraph it works - and by "in the hashgraph" i mean has linkage via parents to the conceptual hashgraph formed by the network, not the hashgraph module.


It is a theorem of the hashgraph consensus algorithm that an event created by an honest node eventually reaches consensus (with probability 1), where reaching consensus means it either has a consensus timestamp and consensus order agreed to by all nodes, or it becomes stale and all nodes agree it is stale.

Termination rests on two facts. First, virtual voting on each round's witnesses always terminates: a coin round periodically injects a vote drawn from a source no adversary can predict (the `coin`), which with probability 1 eventually breaks any asynchronous stalemate the network might sustain. Second, once witnesses are decided the round has judges, against which every event is either ordered or — if it fails to reach consensus before the advancing ancient boundary — declared stale. The theorem is conditional on the algorithm's standard assumptions (honest stake exceeds two-thirds; an honest event eventually reaches every honest node; and the rest).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is one of the "standard assumptions" that no single node has a super majority of network stake? Is there a list of all "standard assumptions"?

- **A voting rule that need not terminate** — one without the coin's eventual tie-break.
- **An ancient boundary that never advances**, leaving non-consensus events neither ordered nor aged out.

Any change under which an honest event can stay undecided forever defeats liveness: consensus stalls and no transactions finalize.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is true. If an event can stay undecided forever, it does not follow that consensus stalls and no transactions finalize.


## Statement

With probability 1, every round eventually has at least one judge, and this continues for an infinite number of rounds. Round elections do not dry up: the supply of judges that anchors ordering and timestamps never runs out.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming the network is live, i.e. that it is connected and alive. Do you think that needs to be added here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create Invariants Document

4 participants