From 9018e0dfc215c3c3f76519ae0ad527cf2bc68f74 Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sat, 1 Jun 2019 21:00:22 +0530 Subject: [PATCH 01/12] Update README.md Minor changes for my own sake. Not really important --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c57701b..0cce2cf 100644 --- a/README.md +++ b/README.md @@ -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 From b20446296a01d4e696ed542c5419a0c5ccc1cff5 Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sat, 1 Jun 2019 22:38:21 +0530 Subject: [PATCH 02/12] Remodel of block_header code First, There is no need for 'if constructs' inside the @property decorators (getter functions). They will simply increase the overhead of checking that condition each time the function is called. All the assignment can be done the constructor itself. --- blockchain_parser/block_header.py | 47 ++++++++----------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/blockchain_parser/block_header.py b/blockchain_parser/block_header.py index 9e615e8..b3c5cfd 100644 --- a/blockchain_parser/block_header.py +++ b/blockchain_parser/block_header.py @@ -9,25 +9,22 @@ # 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] + self._hex = raw_hex[:80] + self._version, self._previous_block_hash, self._merkle_root,\ + self._timestamp, self._bits, self._nonce = struct.unpack(" Date: Sat, 1 Jun 2019 22:48:56 +0530 Subject: [PATCH 03/12] Remodel of block_header.py : Fixed PEP8 Warnings --- blockchain_parser/block_header.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blockchain_parser/block_header.py b/blockchain_parser/block_header.py index b3c5cfd..20221b3 100644 --- a/blockchain_parser/block_header.py +++ b/blockchain_parser/block_header.py @@ -39,28 +39,34 @@ def from_hex(cls, raw_hex): def version(self): """Return the block's version""" return self._version + @property def previous_block_hash(self): """Return the hash of the previous block""" return self._previous_block_hash + @property def merkle_root(self): """Returns the block's merkle root""" return self._merkle_root + @property def timestamp(self, utc = False): """Returns the timestamp of the block as a UTC datetime object""" if utc == True: return datetime.utcfromtimestamp(self._timestamp) return self._timestamp + @property def bits(self): """Returns the bits (difficulty target) of the block""" return self._bits + @property def nonce(self): """Returns the block's nonce""" return self._nonce + @property def difficulty(self): """Returns the block's difficulty target as a float""" From 34cea15dece8abdce96c0ccb254f81ccbdabd5c2 Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sat, 1 Jun 2019 22:55:45 +0530 Subject: [PATCH 04/12] Update block_header.py --- blockchain_parser/block_header.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/blockchain_parser/block_header.py b/blockchain_parser/block_header.py index 20221b3..df8b53d 100644 --- a/blockchain_parser/block_header.py +++ b/blockchain_parser/block_header.py @@ -21,9 +21,15 @@ class BlockHeader(object): def __init__(self, raw_hex): self._hex = raw_hex[:80] - self._version, self._previous_block_hash, self._merkle_root,\ - self._timestamp, self._bits, self._nonce = struct.unpack(" Date: Sat, 1 Jun 2019 23:13:53 +0530 Subject: [PATCH 05/12] Update block_header.py --- blockchain_parser/block_header.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/blockchain_parser/block_header.py b/blockchain_parser/block_header.py index df8b53d..ea4d0e5 100644 --- a/blockchain_parser/block_header.py +++ b/blockchain_parser/block_header.py @@ -39,40 +39,40 @@ def __repr__(self): def from_hex(cls, raw_hex): """Builds a BlockHeader object from its bytes representation""" return cls(raw_hex) - + # GETTER Functions @property def version(self): """Return the block's version""" return self._version - + @property def previous_block_hash(self): """Return the hash of the previous block""" return self._previous_block_hash - + @property def merkle_root(self): """Returns the block's merkle root""" return self._merkle_root - + @property def timestamp(self, utc=False): """Returns the timestamp of the block as a UTC datetime object""" if utc: return datetime.utcfromtimestamp(self._timestamp) return self._timestamp - + @property def bits(self): """Returns the bits (difficulty target) of the block""" return self._bits - + @property def nonce(self): """Returns the block's nonce""" return self._nonce - + @property def difficulty(self): """Returns the block's difficulty target as a float""" From 4217d58603104dc00dff71c1d3ea9f755f823067 Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sat, 1 Jun 2019 23:24:42 +0530 Subject: [PATCH 06/12] Update block.py --- blockchain_parser/block.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain_parser/block.py b/blockchain_parser/block.py index 2ad3263..3122719 100644 --- a/blockchain_parser/block.py +++ b/blockchain_parser/block.py @@ -86,5 +86,5 @@ def transactions(self): def header(self): """Returns a BlockHeader object corresponding to this block""" if self._header is None: - self._header = BlockHeader.from_hex(self.hex[:80]) + self._header = BlockHeader(self.hex[:80]) return self._header From b77f4642f32776bccdc7f9d8ce9a4f61433fa8f4 Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sun, 2 Jun 2019 00:48:12 +0530 Subject: [PATCH 07/12] Response to Issue#32: Let TimeStamp be integer Timestamp can be left as an integer. And it can be interpreted as `datetime.datetime()` object when required to be printed. This is because keeping it as an integer it can be converted into other timestamp formats like the SAS Timestamp format that starts with January 1, 1960, unlike the UNIX Time. In this way, this parser can be used with other software also. --- blockchain_parser/block_header.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/blockchain_parser/block_header.py b/blockchain_parser/block_header.py index ea4d0e5..ad95d32 100644 --- a/blockchain_parser/block_header.py +++ b/blockchain_parser/block_header.py @@ -57,10 +57,8 @@ def merkle_root(self): return self._merkle_root @property - def timestamp(self, utc=False): + def timestamp(self): """Returns the timestamp of the block as a UTC datetime object""" - if utc: - return datetime.utcfromtimestamp(self._timestamp) return self._timestamp @property From ebeb53cdbffb47ee2f0a649cba30a857bd8ba994 Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sun, 2 Jun 2019 00:51:03 +0530 Subject: [PATCH 08/12] Time Made Integer --- blockchain_parser/tests/test_block.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain_parser/tests/test_block.py b/blockchain_parser/tests/test_block.py index 3cdc540..659a247 100644 --- a/blockchain_parser/tests/test_block.py +++ b/blockchain_parser/tests/test_block.py @@ -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) From 38ced6bbe2a3b01ecd306c493baff5dd5dea3e1f Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sun, 2 Jun 2019 01:00:32 +0530 Subject: [PATCH 09/12] `@classmethod fromhex()` & `__repr__()` not needed --- blockchain_parser/block_header.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/blockchain_parser/block_header.py b/blockchain_parser/block_header.py index ad95d32..29f8110 100644 --- a/blockchain_parser/block_header.py +++ b/blockchain_parser/block_header.py @@ -32,14 +32,6 @@ def __init__(self, raw_hex): format_hash(self._merkle_root) self._difficulty = CBlockHeader.calc_difficulty(self._bits) - 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) - # GETTER Functions @property def version(self): From 3512396ce5db9d9db3e9c23d90b5f17124a3c1db Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sun, 2 Jun 2019 01:19:21 +0530 Subject: [PATCH 10/12] `__repr__()` was not getting coverage --- blockchain_parser/block.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/blockchain_parser/block.py b/blockchain_parser/block.py index 3122719..f6f17ee 100644 --- a/blockchain_parser/block.py +++ b/blockchain_parser/block.py @@ -47,9 +47,6 @@ def __init__(self, raw_hex, height = None): 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""" From 80e61fc397a5d58264a3dbb4766b2eb2bb2af9e0 Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sun, 2 Jun 2019 01:31:09 +0530 Subject: [PATCH 11/12] Remodel of Class Block Similar to `block_header.py`, `if constructs` are not needed in the `getter methods`. All those assignments can be done in the constructor itself. --- blockchain_parser/block.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/blockchain_parser/block.py b/blockchain_parser/block.py index f6f17ee..378a9cc 100644 --- a/blockchain_parser/block.py +++ b/blockchain_parser/block.py @@ -40,23 +40,16 @@ 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 - @classmethod - def from_hex(cls, raw_hex): - """Builds a block object from its bytes representation""" - return 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 @@ -65,23 +58,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(self.hex[:80]) return self._header From 57474210ca171a87c0374f00b07b81509b1cf199 Mon Sep 17 00:00:00 2001 From: tejaskhajanchee Date: Sun, 2 Jun 2019 01:44:10 +0530 Subject: [PATCH 12/12] Update block.py --- blockchain_parser/block.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/blockchain_parser/block.py b/blockchain_parser/block.py index 378a9cc..af215b1 100644 --- a/blockchain_parser/block.py +++ b/blockchain_parser/block.py @@ -47,6 +47,11 @@ def __init__(self, raw_hex, height = None): self.size = len(raw_hex) self.height = height + @classmethod + def from_hex(cls, raw_hex): + """Builds a block object from its bytes representation""" + return cls(raw_hex) + @property def hash(self): """Returns the block's hash (double sha256 of its 80 bytes header"""