Skip to content

Commit e3a9750

Browse files
authored
Merge pull request #88 from earce/master
Adding some transaction specific functionality to library
2 parents d028c68 + 436bf04 commit e3a9750

File tree

4 files changed

+76
-6
lines changed

4 files changed

+76
-6
lines changed

blockchain_parser/blockchain.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
import stat
1717
import plyvel
1818

19+
from blockchain_parser.transaction import Transaction
20+
from blockchain_parser.index import DBTransactionIndex
21+
from blockchain_parser import utils
22+
from binascii import unhexlify
23+
from binascii import hexlify
1924
from .block import Block
2025
from .index import DBBlockIndex
2126
from .utils import format_hash
@@ -146,7 +151,6 @@ def _index_confirmed(self, chain_indexes, num_confirmations=6):
146151
if len(chain) == num_confirmations:
147152
return first_block.hash in chain
148153

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

171-
# remove small forks that may have occured while the node was live.
172-
# Occassionally a node will receive two different solutions to a block
175+
# remove small forks that may have occurred while the node was live.
176+
# Occasionally a node will receive two different solutions to a block
173177
# at the same time. The Leveldb index saves both, not pruning the
174178
# block that leads to a shorter chain once the fork is settled without
175179
# "-reindex"ing the bitcoind block data. This leads to at least two
@@ -217,3 +221,36 @@ def get_ordered_blocks(self, index, start=0, end=None, cache=None):
217221
break
218222
blkFile = os.path.join(self.path, "blk%05d.dat" % blkIdx.file)
219223
yield Block(get_block(blkFile, blkIdx.data_pos), blkIdx.height)
224+
225+
def get_transaction(self, txid, db):
226+
"""Yields the transaction contained in the .blk files as a python
227+
object, similar to
228+
https://developer.bitcoin.org/reference/rpc/getrawtransaction.html
229+
"""
230+
231+
byte_arr = bytearray.fromhex(txid)
232+
byte_arr.reverse()
233+
tx_hash = hexlify(b't').decode('utf-8') + \
234+
hexlify(byte_arr).decode('utf-8')
235+
236+
tx_hash_fmtd = unhexlify(tx_hash)
237+
raw_hex = db.get(tx_hash_fmtd)
238+
239+
tx_idx = DBTransactionIndex(utils.format_hash(tx_hash_fmtd), raw_hex)
240+
blk_file = os.path.join(self.path, "blk%05d.dat" % tx_idx.blockfile_no)
241+
raw_hex = get_block(blk_file, tx_idx.file_offset)
242+
243+
offset = tx_idx.block_offset
244+
245+
transaction_data = raw_hex[80:]
246+
# Try from 1024 (1KiB) -> 1073741824 (1GiB) slice widths
247+
for j in range(0, 20):
248+
try:
249+
offset_e = offset + (1024 * 2 ** j)
250+
transaction = Transaction.from_hex(
251+
transaction_data[offset:offset_e])
252+
return transaction
253+
except Exception:
254+
continue
255+
256+
return None

blockchain_parser/index.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def __init__(self, blk_hash, raw_hex):
5151
self.undo_pos, i = _read_varint(raw_hex[pos:])
5252
pos += i
5353

54-
assert(pos + 80 == len(raw_hex))
54+
assert (pos + 80 == len(raw_hex))
5555
self.version, p, m, time, bits, self.nonce = unpack(
5656
"<I32s32sIII",
5757
raw_hex[-80:]
@@ -62,3 +62,20 @@ def __init__(self, blk_hash, raw_hex):
6262
def __repr__(self):
6363
return "DBBlockIndex(%s, height=%d, file_no=%d, file_pos=%d)" \
6464
% (self.hash, self.height, self.file, self.data_pos)
65+
66+
67+
class DBTransactionIndex(object):
68+
def __init__(self, txn_hash, raw_hex):
69+
self.hash = txn_hash
70+
pos = 0
71+
self.blockfile_no, i = _read_varint(raw_hex[pos:])
72+
pos += i
73+
self.file_offset, i = _read_varint(raw_hex[pos:])
74+
pos += i
75+
self.block_offset, i = _read_varint(raw_hex[pos:])
76+
77+
def __repr__(self):
78+
return "DBTransactionIndex(%s, blockfile_no=%d, " \
79+
"file_offset=%d, block_offset=%d)" \
80+
% (self.hash, self.blockfile_no,
81+
self.file_offset, self.block_offset)

blockchain_parser/output.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __repr__(self):
3838

3939
@property
4040
def value(self):
41-
"""Returns the value of the output exprimed in satoshis"""
41+
"""Returns the value of the output expressed in satoshis"""
4242
if self._value is None:
4343
self._value = decode_uint64(self._value_hex)
4444
return self._value
@@ -52,7 +52,7 @@ def script(self):
5252

5353
@property
5454
def addresses(self):
55-
"""Returns a list containinng all the addresses mentionned
55+
"""Returns a list containing all the addresses mentioned
5656
in the output's script
5757
"""
5858
if self._addresses is None:

blockchain_parser/tests/test_index.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from binascii import a2b_hex
33

44
from blockchain_parser.index import DBBlockIndex
5+
from blockchain_parser.index import DBTransactionIndex
56

67

78
class TestDBIndex(unittest.TestCase):
@@ -30,3 +31,18 @@ def test_from_hex(self):
3031
"b593a8e50c3805ffae1319275fb")
3132
self.assertEqual(idx.merkle_root, "e34721a2587695e74caf820006d2e8c1f5f"
3233
"54350b49d97e74b26f87ac66b1cc1")
34+
35+
36+
class TestDBTransactionIndex(unittest.TestCase):
37+
def test_from_hex(self):
38+
key_str = "70ad7da56decc86b8a58ac53dbde792c9e97552cdaafd37312af7c4" \
39+
"d5c7d0cc1"
40+
value_str = "9071938b980ba4bf39"
41+
42+
value_hex = a2b_hex(value_str)
43+
idx = DBTransactionIndex(key_str, value_hex)
44+
45+
self.assertEqual(idx.hash, key_str)
46+
self.assertEqual(idx.blockfile_no, 2289)
47+
self.assertEqual(idx.block_offset, 614457)
48+
self.assertEqual(idx.file_offset, 42142859)

0 commit comments

Comments
 (0)