Skip to content

Instantly share code, notes, and snippets.

@davidlj95
Last active July 21, 2017 05:40
Show Gist options
  • Save davidlj95/e15d73618c3280a04ac022f4aa8a1c21 to your computer and use it in GitHub Desktop.
Save davidlj95/e15d73618c3280a04ac022f4aa8a1c21 to your computer and use it in GitHub Desktop.
Python3 script to obtain interesting data about a Bitcoin block (mainly what it signals) given the block hash, by using RPC calls to bitcoind
#!/usr/bin/env python3
# Description:
# ============
# Script to given a block hash, create a message informing
# if the block signals for BIP-91 / BIP-141
#
# To do that, connects to the local bitcoind node and
# uses RPC calls to get details on that block and what does
# it signal
#
# Feel free to modify the output message with the details
# you want
#
# This is intended to use along `-blocknotify` to notify via
# Telegram using telegram-cli project when a new block
# appears and if that block signals something or not
#
# Requirements:
# =============
# In order to work, the script requires the RPC binding to
# access the RPC server in a bitcoind node. Libraries
# needed are then:
#
# - python-bitcoinlib
#
# You can install them with
# `pip install python-bitcoinlib`
#
# This library requires `libssl-dev`, you can install
# it using apt
#
# Usage:
# ======
# ./block-info.py <block_hash>
#
# Libraries
import sys
import bitcoin
import bitcoin.rpc
import logging
# Constants
# VERSION_BITS (dict): What does each version bit mean
# Key is the BIP that signals
# Value is version bit to signal it
VERSION_BITS = {
# BIP-91 signals with 4th bit
"BIP91": 4,
# BIP-141 signals with 1st bit
"BIP141": 1,
}
# VERSION_BITS_MASK (dict): Same as before but with masks
# So we can easily check with a
# bitwise AND if a version signals
# or not each BIP
VERSION_BITS_MASK = dict((key, 1 << value) \
for key,value in VERSION_BITS.items())
# VERSION_MASK (int): Mask to use to get the real version
# from the version field, separing from
# BIP9 signaling bits (3 first bits)
VERSION_MASK = 0b111 << 28
# Arguments
args = sys.argv[1:] # Omit program name
if len(args) == 0:
print("Error: missing block hash parameter")
print("Usage: %s <block_hash>", sys.argv[0])
sys.exit(1)
block_hash = args[0] # First argument is block hash
# Basic config
# Config
logging.basicConfig(
level=logging.INFO,
format="[%(asctime)s][%(name)s][%(levelname)s] %(message)s"
)
# Check arguments
# # Block hash
try:
block_hash_bytes = bytes.fromhex(block_hash)[::-1]
# We transmit the hash in Big Endian, but require LE
except Exception as e:
logging.error("Invalid hex hash %s: %s",
block_hash, str(e))
sys.exit(1)
# Select network
bitcoin.SelectParams("mainnet")
# Check block hash
proxy = bitcoin.rpc.Proxy()
try:
block = proxy.getblock(block_hash_bytes)
except IndexError as e:
logging.error("Unable to find block with hash %s: %s",
block_hash, str(e))
sys.exit(1)
# Get block version
version = block.get_header().nVersion
version_hex = hex(version)
version_num = (version & VERSION_MASK) >> 28
# Get signalings
signalings = []
for bip, mask in VERSION_BITS_MASK.items():
# Check if mask appies
if version & mask != 0:
# Signals that BIP
signalings.append(bip)
# Get coinbase
# Src:
# https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki
coinbase_tx = block.vtx[0]
coinbase = block.vtx[0].vin[0].scriptSig
coinbase_data = [x for x in coinbase]
# # Coinbase number of block
coinbase_field_num = int.from_bytes(coinbase_data[0], "little")
coinbase_extras = filter(None, [
None if isinstance(x, int) else \
# We do not need nums
None if not all(c < 128 for c in x) \
# We do not need bytes
else x.decode("ascii")
# We want ASCIIs!
for x in coinbase_data[1:]])
coinbase_mean = "%s|%s" % (
coinbase_field_num,
"|".join(coinbase_extras))
coinbase_rewards = sum(out.nValue / 1e8 for out in coinbase_tx.vout)
# Information
print("-> BLOCK HASH: %s" % block_hash)
print("-> VERSION field: %s (%d)" % (version_hex, version))
print("-> VERSION num: %d" % version_num)
print("-> TXs: %d (%f BTC fees)" % (
len(block.vtx),
coinbase_rewards - 12.5))
print("-> SIGNALS: %s" % str(signalings))
print("-> COINBASE: %s" % coinbase_mean)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment