Skip to content

blockchain: Add basic historical agenda support. - #3791

Open
davecgh wants to merge 13 commits into
decred:masterfrom
davecgh:blockchain_historical_agendas
Open

blockchain: Add basic historical agenda support.#3791
davecgh wants to merge 13 commits into
decred:masterfrom
davecgh:blockchain_historical_agendas

Conversation

@davecgh

@davecgh davecgh commented Sep 9, 2026

Copy link
Copy Markdown
Member

This consists of a series of commits that culminate in adding basic support for hard-coded known historical activation points that are now facts of the chain and adds the details for all historical agendas on the main and version 3 test networks.

It introduces a new positional agenda state query that attempts to determine the status of an agenda for a given block using only information available that depends on its position within the block chain and the headers of all ancestors.

The new positional query uses the historical data, when present, to definitively determine whether or not an agenda is active without counting votes.

Concretely, the new positional query is able to provide a definitive result in the following circumstances:

  • The agenda is forced active
  • The parent of the activation block is a historical fact
  • The activation has been opportunistically discovered while tallying votes in a full-context path

Ideally, a historical agenda should be able to stand in for a deployment entirely in addition to working alongside one when present. However, the ability to query state changes and vote information via various methods currently depends on agendas having an associated deployment. So, this retains the requirement for agendas to have associated deployment information unless they also have a forced state.

Nevertheless, it is still a useful incremental improvement that paves the way for more efficient and stronger validation of approved historical agendas.

There is a fair amount of preparatory work in the earlier commits such that each commit is a self-contained and logical change that fully builds and passes all tests. An overview of the progression is:

  • Preparation that moves function independently to simplify diffs
  • Consolidates active agenda determination
  • Makes a couple of early ad-hoc agendas use the consolidated infrastructure
  • Adds tests for one of the earlier testnet-only agendas that did not have any
  • Changes the threshold state tuple to use an ID instead of a pointer to a chaincfg.Choice
  • Separates the agenda and deployment logic
  • Adds default required agendas along with tests
  • Adds basic historical agenda support along with tests

See each commit message for full details.

Determining agendas requires taking the chain lock for write since it
can mutate the threshold state cache.

Strictly speaking, there is not actually a race with the current code
despite not taking the expected lock and locking mode in the updated
cases due to the interaction of other locks and semantics.  However, it
is both safer and more accurate to take the expected lock as required
versus relying on what is essentially invisible behavior of other locking
preventing a race.

While here, update various function comments to call out the required
locking behavior.
Currently, deployments are overloaded with different concerns that are
actually conceptually distinct as they relate to consensus changes.
This overloading obscures the different concepts and, more importantly,
makes it difficult to cleanly handle changes that only affect one of
them.

Concretely, the first distinct concept is the specific mechanism used to
decide on whether or not the consensus change should be applied.  It
involves tallying votes and making a decision based on those votes.

The second distinct concept is the existence of a consensus rule that
can change depending on its status at a given point in the chain.

With the goal of cleanly separating the distinct concerns, this is the
first in a series of commits that will eventually make agendas a
separate entity that reference deployments instead of using a single
entity for both.

This commit moves all methods related to querying the state of an agenda
to a separate file along with supporting code.  It also moves various
code related to deployments to the new file and the associated
deployment validation test to match and updates a couple of comments
while here.  It is done first to avoid cluttering diffs.
This consolidates the logic that determines if an agenda with a single
winning choice is active.  That applies to all current agendas and will
likely apply to the majority of future agendas too.

A separate function can be added in the future if any agendas with more
than one possible winning choice are introduced.

The primary motivation is to help pave the way for splitting agendas and
deployments into separate entities, but it is also useful on its own
because a single source for the primary determination logic is more
convenient, less error prone, and less overall code.

This is part of a series of commits to make agendas a separate entity
that references deployments instead of using a single entity for both.
This modifies the code that chooses the stake difficulty algorithm based
on the state of the agenda to be consistent with the way almost all of
the other agendas are handled and to make use of the new consolidated
agenda status determination approach.

Since the agenda state query now returns an error, the methods that use
it are updated to propagate the error up the call chain.

This is part of a series of commits to make agendas a separate entity
that references deployments instead of using a single entity for both.
This modifies the code that chooses the maximum block size based on the
state of the agenda to be more consistent with the way the other agendas
are handled and to make use of the new consolidated agenda status
determination approach.

Since the agenda state query now returns an error, the methods that use
it are updated to propagate the error up the call chain.  It also
corrects the comment on maxBlockSize to accurately note that it requires
the chain lock held for write as opposed to reads.

The result for this particular agenda is used differently than the
others, so the status determination retains the slight semantic
difference in order to keep the existing semantics.

A more generalized approach for all agendas is needed in the future to
remove the final remaining inconsistency.

This is part of a series of commits to make agendas a separate entity
that references deployments instead of using a single entity for both.
This adds tests for the updated max block size selection logic.
This updates the threshold state tuple that identifies a winning choice
to use an ID instead of a pointer to the winning choice within the
deployment and updates all consumers and tests accordingly.

This is part of a series of commits to make agendas a separate entity
that references deployments instead of using a single entity for both.
@davecgh davecgh added this to the 2.2.0 milestone Sep 9, 2026
@davecgh
davecgh force-pushed the blockchain_historical_agendas branch from c7d154b to 74a376b Compare September 9, 2026 02:09
This is the final commit in the series to make agendas a separate entity
that references deployments instead of using a single entity for both.

First, it introduces a new struct to house agenda information which
consists of an optional forced state and an optional associated
deployment and removes the forced state from the deployment info.  The
fact that the deployment is now optional is one of the most important
conceptual changes.

Next, it replaces the deployments map with an agendas map that is
constructed by creating new agendas for each deployment defined in the
provided chain parameters and renames various instances of deployment
IDs to indicate they are now agenda IDs.  While the IDs are currently
the same, and likely will be for the foreseeable future, they are
conceptually different, so the rename aims to make that fact more
obvious.

Finally, since the associated deployment information for an agenda is no
longer required in all cases, the functions related to determining when
the state last changed add an additional check to ensure the agenda has
the necessary deployment data.  These functions, in particular,
highlight part of the conceptual difference.  Namely, they are related
to querying details about the deployment process as opposed to the
status of the agenda itself.
The current code requires all behavior that is based on the result of a
consensus change vote to include deployment and voting information in
the chain parameters for all networks because the available agendas are
dynamically built from them.  There is nothing functionally wrong with
that, but it does have some limitations.

One notable limitation is that the deployments can never be removed even
when their historical activation points are already known because the
code requires their presence to determine the status.

Another notable limitation is related to non-main networks, such as
newer versions of testnet and the simulation network.  They both need to
grandfather in previous agendas so that all of the latest rules are
applied by default.  That currently requires all of those older
deployments to be manually specified in multiple chain params and given
a forced choice to make them active.

This provides an incremental improvement to the handling by adding a map
of all required IDs for agendas that influence consensus behavior and
modifies the agenda initialization logic to create a default agenda
entry for all required agendas that do not already have an associated
deployment in the network chain parameters.  The default agendas will
always be inactive for the main network and active for all other
networks.

Finally, it removes a couple of special cases that implemented the same
behavior in an ad-hoc fashion since it is now the default for all
otherwise unspecified required agendas.
This adds a test to ensure the required default agendas are created with
the expected states when no deployments are specified for them in the
chain parameters.
While the existing agenda state determination once an agenda becomes
active is reasonably efficient, it still requires adding entries to the
threshold state cache at every rule change interval in order to
propagate the terminal state forward.  Anchor points for active agendas
allow a more efficient approach for querying since they can be
implemented with a single pointer that only has to be set once.

Aside from allowing for more efficient handling in the general case,
anchor points also have the important property that they do not directly
depend on rule change intervals.  This property means they can be set
independently of vote counting when the information can be determined
via other means.  For example, when historical facts about activation
points are known.  While this commit does not yet take advantage of the
property, it is worth mentioning because it is part of the motivation
for implementing them.

With that in mind, this modifies the tracked information about agendas
to support a cached anchor point that corresponds to the parent of the
block at which the agenda activated.

The active anchor is discovered opportunistically when tallying votes in
a full-context path.

Finally, the consolidated path for querying an agenda state is updated
to use the anchor when it is set to immediately return the active state
for all descendants of the anchor.  The state query falls back to the
normal mechanism based on the threshold state cache when the anchor is
not set or usable.
This makes use of the new active agenda anchors to add basic support for
hard-coded known historical activation points that are now facts of the
chain and adds the details for all historical agendas on the main and
version 3 test networks.

It introduces a new positional agenda state query that attempts to
determine the status of an agenda for a given block using only
information available that depends on its position within the block
chain and the headers of all ancestors.

The new positional query uses the historical data, when present, to
definitively determine whether or not an agenda is active without
counting votes.

Concretely, the new positional query is able to provide a definitive
result in the following circumstances:

- The agenda is forced active
- The parent of the activation block is a historical fact
- The activation has been opportunistically discovered while tallying
  votes in a full-context path

Ideally, a historical agenda should be able to stand in for a deployment
entirely in addition to working alongside one when present.  However,
the ability to query state changes and vote information via various
methods currently depends on agendas having an associated deployment.
So, this retains the requirement for agendas to have associated
deployment information unless they also have a forced state.

Nevertheless, it is still a useful incremental improvement that paves
the way for more efficient and stronger validation of approved
historical agendas.
This adds extensive tests for the new positional agenda state
determination.  It includes testing the state for the genesis block,
forced states, and historical states.  Historical states are tested
before, exactly at, and after the anchor along with side chains that do
and do not descend from the anchor.

It also doubles as more thorough testing for the new active agenda
anchors.  The existing agenda tests do a good job exercising them for
the normal paths while these tests exercise the edge cases.
@davecgh
davecgh force-pushed the blockchain_historical_agendas branch from 74a376b to 319f034 Compare September 9, 2026 03:13
// - the scenario is exceedingly rare
// - it would add significant cost and complexity to allow the
// anchor to move around because this code path will only ever run
// once per discovered activation due to the threshold state

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Suggested change
// once per discovered activation due to the threshold state
// once per discovered activation due to the threshold state cache.

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.

1 participant