2 # Code for dumping a single block, given its ID (hash)
12 from BCDataStream import *
13 from base58 import public_key_to_bc_address
14 from util import short_hex, long_hex
15 from deserialize import *
17 def _open_blkindex(db_env):
20 r = db.open("blkindex.dat", "main", DB_BTREE, DB_THREAD|DB_RDONLY)
24 logging.error("Couldn't open blkindex.dat/main. Try quitting any running Bitcoin apps.")
28 def _read_CDiskTxPos(stream):
29 n_file = stream.read_uint32()
30 n_block_pos = stream.read_uint32()
31 n_tx_pos = stream.read_uint32()
32 return (n_file, n_block_pos, n_tx_pos)
34 def _dump_block(datadir, nFile, nBlockPos, hash256, hashNext, do_print=True):
35 blockfile = open(os.path.join(datadir, "blk%04d.dat"%(nFile,)), "rb")
37 ds.map_file(blockfile, nBlockPos)
39 block_string = deserialize_Block(d)
43 print "BLOCK "+long_hex(hash256[::-1])
44 print "Next block: "+long_hex(hashNext[::-1])
48 def _parse_block_index(vds):
50 d['version'] = vds.read_int32()
51 d['hashNext'] = vds.read_bytes(32)
52 d['nFile'] = vds.read_uint32()
53 d['nBlockPos'] = vds.read_uint32()
54 d['nHeight'] = vds.read_int32()
56 header_start = vds.read_cursor
57 d['b_version'] = vds.read_int32()
58 d['hashPrev'] = vds.read_bytes(32)
59 d['hashMerkle'] = vds.read_bytes(32)
60 d['nTime'] = vds.read_int32()
61 d['nBits'] = vds.read_int32()
62 d['nNonce'] = vds.read_int32()
63 header_end = vds.read_cursor
64 d['__header__'] = vds.input[header_start:header_end]
67 def dump_block(datadir, db_env, block_hash):
68 """ Dump a block, given hexadecimal hash-- either the full hash
69 OR a short_hex version of the it.
71 db = _open_blkindex(db_env)
78 key_prefix = "\x0ablockindex"
80 (key, value) = cursor.set_range(key_prefix)
82 while key.startswith(key_prefix):
83 kds.clear(); kds.write(key)
84 vds.clear(); vds.write(value)
86 type = kds.read_string()
87 hash256 = kds.read_bytes(32)
88 hash_hex = long_hex(hash256[::-1])
89 block_data = _parse_block_index(vds)
91 if (hash_hex.startswith(block_hash) or short_hex(hash256[::-1]).startswith(block_hash)):
92 print "Block height: "+str(block_data['nHeight'])
93 _dump_block(datadir, block_data['nFile'], block_data['nBlockPos'], hash256, block_data['hashNext'])
95 (key, value) = cursor.next()
99 def read_block(db_cursor, hash):
100 (key,value) = db_cursor.set_range("\x0ablockindex"+hash)
102 vds.clear(); vds.write(value)
103 block_data = _parse_block_index(vds)
104 block_data['hash256'] = hash
107 def scan_blocks(datadir, db_env, callback_fn):
108 """ Scan through blocks, from last through genesis block,
109 calling callback_fn(block_data) for each.
110 callback_fn should return False if scanning should
111 stop, True if it should continue.
112 Returns last block_data scanned.
114 db = _open_blkindex(db_env)
119 # Read the hashBestChain record:
121 (key, value) = cursor.set_range("\x0dhashBestChain")
123 hashBestChain = vds.read_bytes(32)
125 block_data = read_block(cursor, hashBestChain)
127 while callback_fn(block_data):
128 if block_data['nHeight'] == 0:
130 block_data = read_block(cursor, block_data['hashPrev'])
134 def dump_block_n(datadir, db_env, block_number):
135 """ Dump a block given block number (== height, genesis block is 0)
137 def scan_callback(block_data):
138 return not block_data['nHeight'] == block_number
140 block_data = scan_blocks(datadir, db_env, scan_callback)
142 print "Block height: "+str(block_data['nHeight'])
143 _dump_block(datadir, block_data['nFile'], block_data['nBlockPos'], block_data['hash256'], block_data['hashNext'])
145 def search_blocks(datadir, db_env, pattern):
146 """ Dump a block given block number (== height, genesis block is 0)
148 db = _open_blkindex(db_env)
152 # Read the hashBestChain record:
154 (key, value) = cursor.set_range("\x0dhashBestChain")
156 hashBestChain = vds.read_bytes(32)
157 block_data = read_block(cursor, hashBestChain)
159 if pattern == "NONSTANDARD_CSCRIPTS": # Hack to look for non-standard transactions
160 search_odd_scripts(datadir, cursor, block_data)
164 block_string = _dump_block(datadir, block_data['nFile'], block_data['nBlockPos'],
165 block_data['hash256'], block_data['hashNext'], False)
167 if re.search(pattern, block_string) is not None:
168 print "MATCH: Block height: "+str(block_data['nHeight'])
171 if block_data['nHeight'] == 0:
173 block_data = read_block(cursor, block_data['hashPrev'])
175 def search_odd_scripts(datadir, cursor, block_data):
176 """ Look for non-standard transactions """
178 block_string = _dump_block(datadir, block_data['nFile'], block_data['nBlockPos'],
179 block_data['hash256'], block_data['hashNext'], False)
181 found_nonstandard = False
182 for m in re.finditer(r'TxIn:(.*?)$', block_string, re.MULTILINE):
184 if re.match(r'\s*COIN GENERATED coinbase:\w+$', s): continue
185 if re.match(r'.*sig: \d+:\w+...\w+ \d+:\w+...\w+$', s): continue
186 if re.match(r'.*sig: \d+:\w+...\w+$', s): continue
187 print "Nonstandard TxIn: "+s
188 found_nonstandard = True
191 for m in re.finditer(r'TxOut:(.*?)$', block_string, re.MULTILINE):
193 if re.match(r'.*Script: DUP HASH160 \d+:\w+...\w+ EQUALVERIFY CHECKSIG$', s): continue
194 if re.match(r'.*Script: \d+:\w+...\w+ CHECKSIG$', s): continue
195 print "Nonstandard TxOut: "+s
196 found_nonstandard = True
199 if found_nonstandard:
200 print "NONSTANDARD TXN: Block height: "+str(block_data['nHeight'])
203 if block_data['nHeight'] == 0:
205 block_data = read_block(cursor, block_data['hashPrev'])
207 def check_block_chain(db_env):
208 """ Make sure hashPrev/hashNext pointers are consistent through block chain """
209 db = _open_blkindex(db_env)
214 # Read the hashBestChain record:
216 (key, value) = cursor.set_range("\x0dhashBestChain")
218 hashBestChain = vds.read_bytes(32)
222 block_data = read_block(cursor, hashBestChain)
224 while block_data['nHeight'] > 0:
225 back_blocks.append( (block_data['nHeight'], block_data['hashMerkle'], block_data['hashPrev'], block_data['hashNext']) )
226 block_data = read_block(cursor, block_data['hashPrev'])
228 back_blocks.append( (block_data['nHeight'], block_data['hashMerkle'], block_data['hashPrev'], block_data['hashNext']) )
229 genesis_block = block_data
231 print("check block chain: genesis block merkle hash is: %s"%(block_data['hashMerkle'][::-1].encode('hex_codec')))
233 while block_data['hashNext'] != ('\0'*32):
234 forward = (block_data['nHeight'], block_data['hashMerkle'], block_data['hashPrev'], block_data['hashNext'])
235 back = back_blocks.pop()
237 print("Forward/back block mismatch at height %d!"%(block_data['nHeight'],))
238 print(" Forward: "+str(forward))
239 print(" Back: "+str(back))
240 block_data = read_block(cursor, block_data['hashNext'])