Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding some transaction specific functionality to library #88

Merged
merged 4 commits into from
Jan 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions blockchain_parser/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
import stat
import plyvel

from blockchain_parser.transaction import Transaction
from blockchain_parser.index import DBTransactionIndex
from blockchain_parser import utils
from binascii import unhexlify
from binascii import hexlify
from .block import Block
from .index import DBBlockIndex
from .utils import format_hash
Expand Down Expand Up @@ -146,7 +151,6 @@ def _index_confirmed(self, chain_indexes, num_confirmations=6):
if len(chain) == num_confirmations:
return first_block.hash in chain


def get_ordered_blocks(self, index, start=0, end=None, cache=None):
"""Yields the blocks contained in the .blk files as per
the heigt extract from the leveldb index present at path
Expand All @@ -168,8 +172,8 @@ def get_ordered_blocks(self, index, start=0, end=None, cache=None):
with open(cache, 'wb') as f:
pickle.dump(blockIndexes, f)

# remove small forks that may have occured while the node was live.
# Occassionally a node will receive two different solutions to a block
# remove small forks that may have occurred while the node was live.
# Occasionally a node will receive two different solutions to a block
# at the same time. The Leveldb index saves both, not pruning the
# block that leads to a shorter chain once the fork is settled without
# "-reindex"ing the bitcoind block data. This leads to at least two
Expand Down Expand Up @@ -217,3 +221,36 @@ def get_ordered_blocks(self, index, start=0, end=None, cache=None):
break
blkFile = os.path.join(self.path, "blk%05d.dat" % blkIdx.file)
yield Block(get_block(blkFile, blkIdx.data_pos), blkIdx.height)

def get_transaction(self, txid, db):
"""Yields the transaction contained in the .blk files as a python
object, similar to
https://developer.bitcoin.org/reference/rpc/getrawtransaction.html
"""

byte_arr = bytearray.fromhex(txid)
byte_arr.reverse()
tx_hash = hexlify(b't').decode('utf-8') + \
hexlify(byte_arr).decode('utf-8')

tx_hash_fmtd = unhexlify(tx_hash)
raw_hex = db.get(tx_hash_fmtd)

tx_idx = DBTransactionIndex(utils.format_hash(tx_hash_fmtd), raw_hex)
blk_file = os.path.join(self.path, "blk%05d.dat" % tx_idx.blockfile_no)
raw_hex = get_block(blk_file, tx_idx.file_offset)

offset = tx_idx.block_offset

transaction_data = raw_hex[80:]
# Try from 1024 (1KiB) -> 1073741824 (1GiB) slice widths
for j in range(0, 20):
try:
offset_e = offset + (1024 * 2 ** j)
transaction = Transaction.from_hex(
transaction_data[offset:offset_e])
return transaction
except Exception:
continue

return None
19 changes: 18 additions & 1 deletion blockchain_parser/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def __init__(self, blk_hash, raw_hex):
self.undo_pos, i = _read_varint(raw_hex[pos:])
pos += i

assert(pos + 80 == len(raw_hex))
assert (pos + 80 == len(raw_hex))
self.version, p, m, time, bits, self.nonce = unpack(
"<I32s32sIII",
raw_hex[-80:]
Expand All @@ -62,3 +62,20 @@ def __init__(self, blk_hash, raw_hex):
def __repr__(self):
return "DBBlockIndex(%s, height=%d, file_no=%d, file_pos=%d)" \
% (self.hash, self.height, self.file, self.data_pos)


class DBTransactionIndex(object):
def __init__(self, txn_hash, raw_hex):
self.hash = txn_hash
pos = 0
self.blockfile_no, i = _read_varint(raw_hex[pos:])
pos += i
self.file_offset, i = _read_varint(raw_hex[pos:])
pos += i
self.block_offset, i = _read_varint(raw_hex[pos:])

def __repr__(self):
return "DBTransactionIndex(%s, blockfile_no=%d, " \
"file_offset=%d, block_offset=%d)" \
% (self.hash, self.blockfile_no,
self.file_offset, self.block_offset)
4 changes: 2 additions & 2 deletions blockchain_parser/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __repr__(self):

@property
def value(self):
"""Returns the value of the output exprimed in satoshis"""
"""Returns the value of the output expressed in satoshis"""
if self._value is None:
self._value = decode_uint64(self._value_hex)
return self._value
Expand All @@ -52,7 +52,7 @@ def script(self):

@property
def addresses(self):
"""Returns a list containinng all the addresses mentionned
"""Returns a list containing all the addresses mentioned
in the output's script
"""
if self._addresses is None:
Expand Down
16 changes: 16 additions & 0 deletions blockchain_parser/tests/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from binascii import a2b_hex

from blockchain_parser.index import DBBlockIndex
from blockchain_parser.index import DBTransactionIndex


class TestDBIndex(unittest.TestCase):
Expand Down Expand Up @@ -30,3 +31,18 @@ def test_from_hex(self):
"b593a8e50c3805ffae1319275fb")
self.assertEqual(idx.merkle_root, "e34721a2587695e74caf820006d2e8c1f5f"
"54350b49d97e74b26f87ac66b1cc1")


class TestDBTransactionIndex(unittest.TestCase):
def test_from_hex(self):
key_str = "70ad7da56decc86b8a58ac53dbde792c9e97552cdaafd37312af7c4" \
"d5c7d0cc1"
value_str = "9071938b980ba4bf39"

value_hex = a2b_hex(value_str)
idx = DBTransactionIndex(key_str, value_hex)

self.assertEqual(idx.hash, key_str)
self.assertEqual(idx.blockfile_no, 2289)
self.assertEqual(idx.block_offset, 614457)
self.assertEqual(idx.file_offset, 42142859)