Skip to content

Instantly share code, notes, and snippets.

@johnzweng
Last active July 30, 2026 12:42
Show Gist options
  • Select an option

  • Save johnzweng/cd89bc568a0ea4c34566bc93e35fae0b to your computer and use it in GitHub Desktop.

Select an option

Save johnzweng/cd89bc568a0ea4c34566bc93e35fae0b to your computer and use it in GitHub Desktop.
Check BIP 110 signalling in block headers on your own node
#!/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