From ce8fb3558a1f53b60af7eeda509c6e7b3270aa82 Mon Sep 17 00:00:00 2001 From: Nathan Barraille Date: Sat, 29 Jun 2019 20:15:29 +0200 Subject: [PATCH] Added support for altcoins --- blockchain_parser/__init__.py | 24 ++++++++++++++++++++++++ blockchain_parser/address.py | 18 ++++++++++++------ blockchain_parser/block.py | 18 ++++++++++-------- blockchain_parser/blockchain.py | 25 ++++++++++++------------- blockchain_parser/output.py | 24 ++++++++++++++---------- blockchain_parser/transaction.py | 21 +++++++++++---------- 6 files changed, 83 insertions(+), 47 deletions(-) diff --git a/blockchain_parser/__init__.py b/blockchain_parser/__init__.py index a584d3c..c8a9374 100644 --- a/blockchain_parser/__init__.py +++ b/blockchain_parser/__init__.py @@ -9,4 +9,28 @@ # modified, propagated, or distributed except according to the terms contained # in the LICENSE file. + __version__ = "0.1.4" + + +class BlockchainType(): + def __init__(self, symbol, block_delim, p2pkh_addr_version, p2sh_addr_version): + self.symbol = symbol + self.block_delim = block_delim + self.p2pkh_addr_version = p2pkh_addr_version + self.p2sh_addr_version = p2sh_addr_version + + +BITCOIN = BlockchainType('btc', b'\xf9\xbe\xb4\xd9', b'\x00', b'\x05') + +DASH = BlockchainType('dash', b"\xbf\x0c\x6b\xbd", b'\x4c', b'\x10') + +DIGIBYTE = BlockchainType('zec', b'\xfa\xc3\xb6\xda', b'\x1e', b'\x05') + +DOGECOIN = BlockchainType('doge', b'\xc0\xc0\xc0\xc0', b'\x1e', b'\x16') + +LITECOIN = BlockchainType('ltc', b"\xfb\xc0\xb6\xdb", b'\x30', b'\x05') + +MONACOIN = BlockchainType('mona', b"\xfb\xc0\xb6\xdb", b'\x32', b'\x05') + +ZCASH = BlockchainType('zec', b'\x24\xe9\x27\x64', b'\x1c\xb8', b'\x1c\xbd') diff --git a/blockchain_parser/address.py b/blockchain_parser/address.py index b99a06a..7587600 100644 --- a/blockchain_parser/address.py +++ b/blockchain_parser/address.py @@ -10,32 +10,35 @@ # in the LICENSE file. from bitcoin import base58 + +from .blockchain import BITCOIN from .utils import btc_ripemd160, double_sha256 class Address(object): """Represents a bitcoin address""" - def __init__(self, hash, public_key, address, type): + def __init__(self, hash, public_key, address, type, blockchain_type): self._hash = hash self.public_key = public_key self._address = address self.type = type + self.blockchain_type = blockchain_type def __repr__(self): return "Address(addr=%s)" % self.address @classmethod - def from_public_key(cls, public_key): + def from_public_key(cls, public_key, blockchain_type): """Constructs an Address object from a public key""" - return cls(None, public_key, None, "normal") + return cls(None, public_key, None, "normal", blockchain_type=blockchain_type) @classmethod - def from_ripemd160(cls, hash, type="normal"): + def from_ripemd160(cls, hash, type="normal", blockchain_type=BITCOIN): """Constructs an Address object from a RIPEMD-160 hash, it may be a normal address or a P2SH address, the latter is indicated by setting type to 'p2sh'""" - return cls(hash, None, None, type) + return cls(hash, None, None, type, blockchain_type=blockchain_type) @property def hash(self): @@ -49,7 +52,10 @@ def hash(self): def address(self): """Returns the base58 encoded representation of this address""" if self._address is None: - version = b'\x00' if self.type == "normal" else b'\x05' + if self.type == 'normal': + version = self.blockchain_type.p2pkh_addr_version + else: + version = self.blockchain_type.p2sh_addr_version checksum = double_sha256(version + self.hash) self._address = base58.encode(version + self.hash + checksum[:4]) diff --git a/blockchain_parser/block.py b/blockchain_parser/block.py index f00db41..cb7d29c 100644 --- a/blockchain_parser/block.py +++ b/blockchain_parser/block.py @@ -9,12 +9,12 @@ # modified, propagated, or distributed except according to the terms contained # in the LICENSE file. -from .transaction import Transaction from .block_header import BlockHeader -from .utils import format_hash, decode_varint, double_sha256 +from .transaction import Transaction +from .utils import decode_varint, double_sha256, format_hash -def get_block_transactions(raw_hex): +def get_block_transactions(raw_hex, blockchain_type): """Given the raw hexadecimal representation of a block, yields the block's transactions """ @@ -31,7 +31,7 @@ def get_block_transactions(raw_hex): try: offset_e = offset + (1024 * 2 ** j) transaction = Transaction.from_hex( - transaction_data[offset:offset_e]) + transaction_data[offset:offset_e], blockchain_type) yield transaction break except: @@ -46,8 +46,9 @@ class Block(object): Represents a Bitcoin block, contains its header and its transactions. """ - def __init__(self, raw_hex, height=None): + def __init__(self, raw_hex, blockchain_type, height=None): self.hex = raw_hex + self.blockchain_type = blockchain_type self._hash = None self._transactions = None self._header = None @@ -59,9 +60,9 @@ def __repr__(self): return "Block(%s)" % self.hash @classmethod - def from_hex(cls, raw_hex): + def from_hex(cls, raw_hex, blockchain_type): """Builds a block object from its bytes representation""" - return cls(raw_hex) + return cls(raw_hex, blockchain_type) @property def hash(self): @@ -86,7 +87,8 @@ 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)) + self._transactions = list( + get_block_transactions(self.hex, self.blockchain_type)) return self._transactions diff --git a/blockchain_parser/blockchain.py b/blockchain_parser/blockchain.py index bb826ae..ff95129 100644 --- a/blockchain_parser/blockchain.py +++ b/blockchain_parser/blockchain.py @@ -8,23 +8,20 @@ # No part of bitcoin-blockchain-parser, including this file, may be copied, # modified, propagated, or distributed except according to the terms contained # in the LICENSE file. - -import os import mmap -import struct +import os import pickle import stat +import struct + import plyvel +from . import BITCOIN from .block import Block from .index import DBBlockIndex from .utils import format_hash -# Constant separating blocks in the .blk files -BITCOIN_CONSTANT = b"\xf9\xbe\xb4\xd9" - - def get_files(path): """ Given the path to the .bitcoin directory, returns the sorted list of .blk @@ -38,7 +35,7 @@ def get_files(path): return sorted(files) -def get_blocks(blockfile): +def get_blocks(blockfile, delim): """ Given the name of a .blk file, for every block contained in the file, yields its raw hexadecimal value @@ -54,7 +51,7 @@ def get_blocks(blockfile): offset = 0 block_count = 0 while offset < (length - 4): - if raw_data[offset:offset+4] == BITCOIN_CONSTANT: + if raw_data[offset:offset+4] == delim: offset += 4 size = struct.unpack("