Last active
July 21, 2017 05:40
-
-
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
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
#!/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