Last active
July 30, 2026 12:42
-
-
Save johnzweng/cd89bc568a0ea4c34566bc93e35fae0b to your computer and use it in GitHub Desktop.
Check BIP 110 signalling in block headers on your own node
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
| #!/bin/bash | |
| # | |
| # This shell script allows you to check BIP 110 signalling on your own | |
| # Bitcoin full node, verified by your own node. | |
| # | |
| # BIP 110 (Reduced Data Temporary Softfork) uses modified BIP9 parameters: | |
| # deployment name: reduced_data | |
| # version bit: 4 | |
| # signalling period: 2016 blocks | |
| # threshold: 1109 blocks (55%) | |
| # start time: 1764547200 (approximately 2025-12-01 UTC) | |
| # mandatory signalling: heights 961632 through 963647, unless already locked in | |
| # maximum activation: height 965664 | |
| # active duration: 52416 blocks | |
| # | |
| # Specification: https://github.com/bitcoin/bips/blob/master/bip-0110.mediawiki | |
| # | |
| # DON'T TRUST, VERIFY! :-) | |
| # | |
| # First check if we have the 'bitcoin-cli' and 'jq' commands available. | |
| if ! command -v bitcoin-cli &> /dev/null | |
| then | |
| echo "command 'bitcoin-cli' could not be found. Are you running this script on a Bitcoin node?" | |
| exit 1 | |
| fi | |
| if ! command -v jq &> /dev/null | |
| then | |
| echo "command 'jq' could not be found. It is needed to parse JSON output. Please install it." | |
| exit 1 | |
| fi | |
| # Parse optional command-line arguments. They may be supplied in either order: | |
| # -v Enable verbose output. | |
| # -datadir=/data/ Use a custom Bitcoin data directory. | |
| VERBOSE=0 | |
| BITCOIN_DATADIR="" | |
| for ARG in "$@" | |
| do | |
| case "$ARG" in | |
| -v) | |
| VERBOSE=1 | |
| ;; | |
| -datadir=*) | |
| BITCOIN_DATADIR=${ARG#-datadir=} | |
| if [[ -z "$BITCOIN_DATADIR" ]] | |
| then | |
| echo "Error: -datadir requires a non-empty path." >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| -h|--help) | |
| echo "Usage: $0 [-v] [-datadir=/path/to/bitcoin/data]" | |
| exit 0 | |
| ;; | |
| *) | |
| echo "Unknown argument: $ARG" >&2 | |
| echo "Usage: $0 [-v] [-datadir=/path/to/bitcoin/data]" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| # Keep the command and its optional argument in an array so paths containing | |
| # whitespace are passed to bitcoin-cli correctly. | |
| BITCOIN_CLI=(bitcoin-cli) | |
| if [[ -n "$BITCOIN_DATADIR" ]] | |
| then | |
| BITCOIN_CLI+=("-datadir=$BITCOIN_DATADIR") | |
| fi | |
| # | |
| # Actual script starts HERE: | |
| # | |
| PERIOD_LENGTH=2016 | |
| SIGNALLING_THRESHOLD=1109 | |
| BIP110_BIT=4 | |
| BIP9_TOP_MASK=$((0xE0000000)) | |
| BIP9_TOP_BITS=$((0x20000000)) | |
| # Get the current validated block height. Unlike the "headers" field, this does | |
| # not include headers for blocks which the node has not downloaded/validated. | |
| CURRENT_BLOCKHEIGHT=$("${BITCOIN_CLI[@]}" getblockcount) | |
| # Find the current retarget/signalling period and its first block. | |
| BLOCK_OFFSET_IN_CURRENT_PERIOD=$((CURRENT_BLOCKHEIGHT % PERIOD_LENGTH)) | |
| START_BLOCK_OF_CURRENT_PERIOD=$((CURRENT_BLOCKHEIGHT - BLOCK_OFFSET_IN_CURRENT_PERIOD)) | |
| BLOCK_COUNT_IN_CURRENT_PERIOD=$((BLOCK_OFFSET_IN_CURRENT_PERIOD + 1)) | |
| # Initialize counters. | |
| BLOCKS_SIGNALLING_FOR_BIP110=0 | |
| BLOCKS_NOT_SIGNALLING_FOR_BIP110=0 | |
| echo "" | |
| echo "**************************************" | |
| echo " Checking BIP 110 signalling status:" | |
| echo "**************************************" | |
| echo "" | |
| echo "In the current signalling period, $BLOCK_COUNT_IN_CURRENT_PERIOD out of $PERIOD_LENGTH blocks" | |
| echo "have been mined. We will check these $BLOCK_COUNT_IN_CURRENT_PERIOD blocks now to see" | |
| echo "whether they signal for BIP 110." | |
| echo "" | |
| # Loop through all blocks mined in the current period. | |
| for (( height=START_BLOCK_OF_CURRENT_PERIOD; height<=CURRENT_BLOCKHEIGHT; height++ )) | |
| do | |
| BLOCK_HASH=$("${BITCOIN_CLI[@]}" getblockhash "$height") | |
| BLOCK_VERSION=$("${BITCOIN_CLI[@]}" getblockheader "$BLOCK_HASH" | jq -r .version) | |
| # Modified BIP9 signalling requires the versionbits top pattern (001) and | |
| # version bit 4. Merely finding bit 4 set is not sufficient. | |
| if (( (BLOCK_VERSION & BIP9_TOP_MASK) == BIP9_TOP_BITS && | |
| (BLOCK_VERSION & (1 << BIP110_BIT)) != 0 )) | |
| then | |
| SIGNALS_BIP110=1 | |
| else | |
| SIGNALS_BIP110=0 | |
| fi | |
| # In default mode, insert a newline after every 56 blocks (2016 / 56 = 36). | |
| BLOCK_NUMBER_IN_PERIOD=$((height - START_BLOCK_OF_CURRENT_PERIOD + 1)) | |
| if [[ $VERBOSE -eq 0 && $BLOCK_NUMBER_IN_PERIOD -gt 1 && $(((BLOCK_NUMBER_IN_PERIOD - 1) % 56)) -eq 0 ]] | |
| then | |
| echo "" | |
| fi | |
| if [[ $VERBOSE -eq 1 ]] | |
| then | |
| echo -n "[$((CURRENT_BLOCKHEIGHT-height)) blocks left to check]: " | |
| fi | |
| if [[ $SIGNALS_BIP110 -eq 1 ]] | |
| then | |
| BLOCKS_SIGNALLING_FOR_BIP110=$((BLOCKS_SIGNALLING_FOR_BIP110 + 1)) | |
| if [[ $VERBOSE -eq 1 ]]; then | |
| echo "Block number $height: β " | |
| else | |
| echo -n "β " | |
| fi | |
| else | |
| BLOCKS_NOT_SIGNALLING_FOR_BIP110=$((BLOCKS_NOT_SIGNALLING_FOR_BIP110 + 1)) | |
| if [[ $VERBOSE -eq 1 ]]; then | |
| echo "Block number $height: π The block does NOT signal BIP 110 support." | |
| else | |
| echo -n "π" | |
| fi | |
| fi | |
| done | |
| # Display a summary. | |
| echo "" | |
| echo "" | |
| echo "*********************************************************************" | |
| echo " SUMMARY:" | |
| echo "" | |
| echo " In the current period, $BLOCK_COUNT_IN_CURRENT_PERIOD out of $PERIOD_LENGTH blocks have been mined." | |
| echo " Of these, $BLOCKS_SIGNALLING_FOR_BIP110 signal BIP 110 support and" | |
| echo " $BLOCKS_NOT_SIGNALLING_FOR_BIP110 do not." | |
| echo "" | |
| if [[ $BLOCKS_SIGNALLING_FOR_BIP110 -lt $SIGNALLING_THRESHOLD ]] | |
| then | |
| echo " The BIP 110 lock-in threshold has not been reached in this period." | |
| echo " $SIGNALLING_THRESHOLD blocks must signal, so $((SIGNALLING_THRESHOLD-BLOCKS_SIGNALLING_FOR_BIP110))" | |
| echo " more signalling blocks are needed." | |
| else | |
| echo " π The BIP 110 threshold of $SIGNALLING_THRESHOLD signalling blocks has been reached!" | |
| echo " The deployment can enter LOCKED_IN at the start of the next retarget period." | |
| echo " It becomes ACTIVE one retarget period after LOCKED_IN." | |
| fi | |
| echo "" | |
| if (( CURRENT_BLOCKHEIGHT >= 961632 && CURRENT_BLOCKHEIGHT <= 963647 )) | |
| then | |
| echo " NOTE: BIP 110's mandatory-signalling window is in progress." | |
| echo " Unless already LOCKED_IN, non-signalling blocks at heights 961632-963647" | |
| echo " are invalid under the proposal." | |
| elif (( CURRENT_BLOCKHEIGHT < 961632 )) | |
| then | |
| echo " Mandatory signalling is scheduled for heights 961632-963647 if lock-in" | |
| echo " has not already occurred." | |
| fi | |
| echo "*********************************************************************" | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment