Skip to content
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# bitcoin-blockchain-parser [![Build Status](https://travis-ci.org/alecalve/python-bitcoin-blockchain-parser.svg?branch=master)](https://travis-ci.org/alecalve/python-bitcoin-blockchain-parser) [![Coverage Status](https://coveralls.io/repos/alecalve/python-bitcoin-blockchain-parser/badge.svg?branch=master&service=github)](https://coveralls.io/github/alecalve/python-bitcoin-blockchain-parser?branch=master)
This Python 3 library provides a parser for the raw data stored by bitcoind.
# python-bitcoin-blockchain-parser [![Build Status](https://travis-ci.org/alecalve/python-bitcoin-blockchain-parser.svg?branch=master)](https://travis-ci.org/alecalve/python-bitcoin-blockchain-parser) [![Coverage Status](https://coveralls.io/repos/alecalve/python-bitcoin-blockchain-parser/badge.svg?branch=master&service=github)](https://coveralls.io/github/alecalve/python-bitcoin-blockchain-parser?branch=master)
This Python 3 library provides a parser for the raw data stored by bitcoind (blk*.dat files).

## Features
- Detects outputs types
Expand Down
21 changes: 4 additions & 17 deletions blockchain_parser/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,13 @@ class Block(object):

def __init__(self, raw_hex, height = None):
self.hex = raw_hex
self._hash = None
self._transactions = None
self._header = None
self._n_transactions = None
self._hash = format_hash(double_sha256(self.hex[:80]))
self._header = BlockHeader(self.hex[:80])
self._n_transactions = decode_varint(self.hex[80:])[0]
self._transactions = list(get_block_transactions(self.hex))
self.size = len(raw_hex)
self.height = height

def __repr__(self):
return "Block(%s)" % self.hash

@classmethod
def from_hex(cls, raw_hex):
"""Builds a block object from its bytes representation"""
Expand All @@ -58,8 +55,6 @@ def from_hex(cls, raw_hex):
@property
def hash(self):
"""Returns the block's hash (double sha256 of its 80 bytes header"""
if self._hash is None:
self._hash = format_hash(double_sha256(self.hex[:80]))
return self._hash

@property
Expand All @@ -68,23 +63,15 @@ def n_transactions(self):
it is faster to use this than to use len(block.transactions)
as there's no need to parse all transactions to get this information
"""
if self._n_transactions is None:
self._n_transactions = decode_varint(self.hex[80:])[0]

return self._n_transactions

@property
def transactions(self):
"""Returns a list of the block's transactions represented
as Transaction objects"""
if self._transactions is None:
self._transactions = list(get_block_transactions(self.hex))

return self._transactions

@property
def header(self):
"""Returns a BlockHeader object corresponding to this block"""
if self._header is None:
self._header = BlockHeader.from_hex(self.hex[:80])
return self._header
51 changes: 15 additions & 36 deletions blockchain_parser/block_header.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,82 +9,61 @@
# modified, propagated, or distributed except according to the terms contained
# in the LICENSE file.

import struct
from datetime import datetime
from bitcoin.core import CBlockHeader

from .utils import decode_uint32, format_hash
from .utils import format_hash


class BlockHeader(object):
"""Represents a block header"""

def __init__(self, raw_hex):
self._version = None
self._previous_block_hash = None
self._merkle_root = None
self._timestamp = None
self._bits = None
self._nonce = None
self._difficulty = None

self.hex = raw_hex[:80]

def __repr__(self):
return "BlockHeader(previous_block_hash=%s)" % self.previous_block_hash

@classmethod
def from_hex(cls, raw_hex):
"""Builds a BlockHeader object from its bytes representation"""
return cls(raw_hex)

self._hex = raw_hex[:80]
self._version,\
self._previous_block_hash,\
self._merkle_root,\
self._timestamp,\
self._bits,\
self._nonce = struct.unpack("<I32s32sIII", self._hex)
self._previous_block_hash,\
self._merkle_root = format_hash(self._previous_block_hash),\
format_hash(self._merkle_root)
self._difficulty = CBlockHeader.calc_difficulty(self._bits)

# GETTER Functions
@property
def version(self):
"""Return the block's version"""
if self._version is None:
self._version = decode_uint32(self.hex[:4])
return self._version

@property
def previous_block_hash(self):
"""Return the hash of the previous block"""
if self._previous_block_hash is None:
self._previous_block_hash = format_hash(self.hex[4:36])
return self._previous_block_hash

@property
def merkle_root(self):
"""Returns the block's merkle root"""
if self._merkle_root is None:
self._merkle_root = format_hash(self.hex[36:68])
return self._merkle_root

@property
def timestamp(self):
"""Returns the timestamp of the block as a UTC datetime object"""
if self._timestamp is None:
self._timestamp = datetime.utcfromtimestamp(
decode_uint32(self.hex[68:72])
)
return self._timestamp

@property
def bits(self):
"""Returns the bits (difficulty target) of the block"""
if self._bits is None:
self._bits = decode_uint32(self.hex[72:76])
return self._bits

@property
def nonce(self):
"""Returns the block's nonce"""
if self._nonce is None:
self._nonce = decode_uint32(self.hex[76:80])
return self._nonce

@property
def difficulty(self):
"""Returns the block's difficulty target as a float"""
if self._difficulty is None:
self._difficulty = CBlockHeader.calc_difficulty(self.bits)

return self._difficulty
2 changes: 1 addition & 1 deletion blockchain_parser/tests/test_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def test_from_hex(self):
self.assertEqual(1, block.header.version)
self.assertEqual(1, block.header.difficulty)
self.assertEqual(285, block.size)
self.assertEqual(datetime.utcfromtimestamp(1231006505),
self.assertEqual(1231006505,
block.header.timestamp)
self.assertEqual("0" * 64, block.header.previous_block_hash)

Expand Down