Skip to content

Heretical Deployments

Anthony Towns edited this page Sep 9, 2022 · 3 revisions

Requirements

Deploying a soft fork for testing on a global signet has different requirements to deploying one on mainnet.

In particular:

  • the codebase must support triggering soft fork activation on signet at different heights, as custom signets may not activate at the same height or time as the global signet

  • in the event of a proposed soft fork being found to be buggy, and the specification being updated incompatibly when the bug is fixed, activated soft forks must be able to be rolled back so that the new version of the specification can be tested without triggering a hard fork event

  • activations should be able to take place relatively quickly, so that testing is convenient, rather than spending months to allow coordination amongst miners and users

To emphasise this difference, we're calling this deployment method "heretical".

Specification

The way heretical deployments are designed is as follows:

  • Four configuration parameters are set:

    • int32_t signal_activate, signal_abandon -- version numbers used to signal activation/abandonment of the soft fork via the block header
    • start_time -- used to control when signalling can begin
    • timeout -- used to set a fixed timeout at which the soft fork will always be automatically abandoned
  • signal_activate and signal_abandon should be derived from the BIP number of the soft fork (as uint16_t) as follows:

    • signal_activate = 0x6000_0000 | (((int32_t)bip)<<8) | (bip_version & 0xFF)
    • signal_abandon = 0x4000_0000 | (((int32_t)bip)<<8) | (bip_version & 0xFF)
  • A state machine similar to BIP9 is introduced, with the state updating every 432 blocks (3 days)

  • The following states are defined:

    • DEFINED - inactive, initial state, waiting for start_time
    • STARTED - inactive, waiting for signal or timeout
    • LOCKED_IN - inactive, will be active next period
    • ACTIVE - active, waiting for signal_abandon or timeout
    • DEACTIVATING - active, but will be abandoned next period
    • ABANDONED - inactive, terminal state
  • The transitions to activate a fork look like DEFINED -> STARTED -> LOCKED_IN -> ACTIVE, with STARTED being triggered by median time reaching start_time and LOCKED_IN being triggered by a block being mined with nVersion == signal_activate.

  • The transitions to abandon a fork look like:

    • DEFINED -> ABANDONED - only if timeout is reached
    • STARTED -> ABANDONED - if timeout is reached or nVersion == signal_abandon is seen (overrides the STARTED -> LOCKED_IN transition if a block with nVersion == signal_activate occurs in the same period)
    • LOCKED_IN or ACTIVE -> DEACTIVATING -> ABANDONED - if timeout is reached or nVersion == signal_abandon is observed in LOCKED_IN or ACTIVE

(Note: there is no option for a signet to transition a soft fork back to ACTIVE if a signal_abandon block is mined. Maybe there should be; and a terminal state should only occur via the timeout? On the other hand, because signet is permissioned, the miners could always trigger a large reorg of the chain to put it back in an earlier state to undo an unwanted soft fork abandonment)

Developing a soft fork

As a proposer of a new soft fork, the idea is that you set deployment parameters like this:

        consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY] = SetupDeployment{
            .bip = 119,
            .bip_version = 0,
            .start = 1654041600, // 2022-06-01
            .timeout = 1969660800, // 2032-06-01
        };

The start time and bip_version should be updated each time the proposal is updated in a way that's not fully compatible; and the timeout should normally be set to 10 years after the start time. Note that this means the start time should almost always be in the past, since the BIP should usually be updated before code is committed and merged.

With 256 values for bip_version available, this allows for an incompatible update approximately every month over a 10 year period, without causing any ambiguity and allowing clients implementing different versions of the spec to be compatible.

Mining a signet

As a miner of a signet, you can support testing a proposed soft fork by doing the following:

  • Updating your mining/block-signing nodes to run bitcoin-inquisition rather than bitcoin core
  • Mining a block with nVersion = signal_activate
  • Waiting up to 6 days for the state to transition from STARTED through LOCKED_IN to ACTIVE. Manually mining up to 864 blocks as quickly as possible can reduce this time, of course.

If a soft fork proposal has an incompatible update, bumping bip_version from A to B, you should proceed as follows:

  • Mine a block with nVersion = signal_abandon_A to signal that you will no longer be supporting the old version of the fork
  • Mine a block with nVersion = signal_activate_B to signal that you will be supporting the new version of the fork.
  • Continue running the software supporting version A while the deployment transitions from ACTIVE through DEACTIVATING until it reaches ABANDONED. These states can be observed by invoking bitcoin-cli -signet getdeploymentinfo
  • Once version A is inactive, upgrade your mining software to support version B.
  • If both signalling blocks were in the same period, version B will already be ACTIVE and ready to use, otherwise it will be LOCKED_IN and ready to use after 432 blocks are mined.

Note that signalling for activation of a new version B should be done after signalling for abandonment of any prior version that was activated. Not doing so may cause a hard fork event, that is it may cause users running versions of bitcoin that enforce version A of the soft fork to cease being able to follow the chain once transactions/blocks following the rules in version B are included in the chain.

(Some consideration should probably be given to how the chain behaves if a bug in the soft fork implementation causes it to be a hard fork compared to bitcoin core's consensus rules)

(The contrib/signet/miner software currently does not have any special support for doing this signalling)

Using a signet

In order to test a proposed soft fork that has been merged into the bitcoin inquisition codebase, a user needs to:

  • switch their software from bitcoin core to bitcoin inquisition
  • use a signet whose miners are activating the soft fork (which can be observed by running bitcoin-cli -signet getdeploymentinfo after syncing)

In the event that a soft fork is updated from version A to version B, users may want to recover any funds they have locked under the version A rules. In that case, they may observe that miners are deactivating version A by watching for the DEACTIVATING state via getdeploymentinfo, and will have 432 blocks in this state to recover their funds. (This feature relies on the signet miners signalling for abandonment of A prior to activation of B, and continuing to run software supporting version A until it is inactive)

In the event that you wish to disable a particular soft fork, despite it being active on the signet you're following, you can deactivate it by using the -renounce option; eg bitcoind -renounce=checktemplateverify.