Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
317 lines (263 sloc) 11.2 KB
"""
Script for selection, download and verification of Bismuth snapshot ledgers
Run the script inside the main Bismuth folder ~/Bismuth
node.py must be stopped when running the script
"""
import sqlite3,base64,hashlib,time,json,requests,tarfile,glob,sys
from quantizer import *
from mining_heavy3 import *
from Cryptodome.Hash import SHA
import os.path
import argparse
POW_FORK = 854660
STEP = 10000 #Print steps
DB_START = 900000
DB_HASH = "20c5c06c60e50d18c3ac50ff545662fd4fea885084691bbf7e8f9cce"
def purge(fileList):
for filePath in fileList:
try:
os.remove(filePath)
except:
print("Error while deleting file : ", filePath)
def check_dupes(db):
with sqlite3.connect(db) as ledger_check:
ledger_check.text_factory = str
h3 = ledger_check.cursor()
h3.execute("SELECT * FROM transactions WHERE signature IN (SELECT signature FROM transactions WHERE signature != '0' GROUP BY signature HAVING COUNT(*) >1)")
results = h3.fetchall()
allowed_dupes = [708334,708335]
bok = True
for result in results:
if result[0] not in allowed_dupes:
print ('Duplicate signature in block ' + result[0])
bok = False
return bok
def hash_blocks_until(db,n):
"""Returns combined hash of all block hashes in db until block_height n
"""
sha224 = hashlib.sha224()
with sqlite3.connect(db) as ledger_check:
ledger_check.text_factory = str
h3 = ledger_check.cursor()
for row in h3.execute("SELECT block_hash FROM transactions where "
"block_height>-{} and block_height<{} order by block_height asc".format(n,n)):
sha224.update(str(row[0]).encode("utf-8"))
return sha224.hexdigest()
def bin_convert(string):
return ''.join(format(ord(x), '8b').replace(' ', '0') for x in string)
def verify_blocks(db,n):
"""Function for verification of block hashes
"""
with sqlite3.connect(db) as ledger_check:
ledger_check.text_factory = str
h3 = ledger_check.cursor()
h4 = ledger_check.cursor()
try:
h3.execute("SELECT * FROM misc order by block_height desc limit 1")
db_rows = h3.fetchone()[0]
print("Number of blocks: {}".format(db_rows))
print_step = n
invalid = 0
db_block_hash_prev = ""
for row in h3.execute("SELECT * FROM transactions WHERE reward != 0 and "
"block_height>={} ORDER BY block_height".format(n-1)):
db_block_height = row[0]
db_block_hash = str(row[7])
if db_block_height>=n:
transaction_list_converted = []
for transaction in h4.execute("SELECT * FROM transactions WHERE "
"block_height={}".format(db_block_height)):
q_received_timestamp = quantize_two(transaction[1])
received_timestamp = '%.2f' % q_received_timestamp
received_address = str(transaction[2])[:56]
received_recipient = str(transaction[3])[:56]
received_amount = '%.8f' % (quantize_eight(transaction[4]))
received_signature_enc = str(transaction[5])[:684]
received_public_key_hashed = str(transaction[6])[:1068]
received_operation = str(transaction[10])
received_openfield = str(transaction[11])
transaction_list_converted.append((received_timestamp,
received_address, received_recipient,
received_amount, received_signature_enc,
received_public_key_hashed, received_operation,
received_openfield))
block_hash = hashlib.sha224((str(transaction_list_converted)
+ db_block_hash_prev).encode("utf-8")).hexdigest()
if block_hash != db_block_hash:
print("Block hash mismatch: {}"
.format(db_block_height))
invalid = invalid + 1
db_block_hash_prev = db_block_hash
if db_block_height > print_step:
print("Block hashes, verified {}".format(print_step))
print_step += STEP
if invalid == 0:
print("All blocks in the local ledger are valid")
else:
print("{} invalid blocks found".format(invalid))
h3.close()
h4.close()
except Exception as e:
print("Error: {}".format(e))
raise
def check_block(block_height_new,miner_address,nonce,db_block_hash,diff0,
received_timestamp,q_received_timestamp,q_db_timestamp_last):
"""Slightly modified version of Eggdrasyl's function check_block
"""
if block_height_new == POW_FORK - 1 :
diff0 = FORK_DIFF
if block_height_new == POW_FORK:
diff0 = FORK_DIFF
bok = False
real_diff = diffme_heavy3(miner_address,nonce,db_block_hash)
diff_drop_time = Decimal(180)
# simplified comparison, no backwards mining
if real_diff >= int(diff0):
bok = True
elif Decimal(received_timestamp) > q_db_timestamp_last + Decimal(diff_drop_time):
# uses block timestamp, don't merge with diff() for security reasons
time_difference = q_received_timestamp - q_db_timestamp_last
diff_dropped = quantize_ten(diff0) + quantize_ten(1) - quantize_ten(time_difference / diff_drop_time)
# Emergency diff drop
if Decimal(received_timestamp) > q_db_timestamp_last + Decimal(2 * diff_drop_time):
factor = 10
diff_dropped = quantize_ten(diff0) - quantize_ten(1) - quantize_ten(factor
* (time_difference-2*diff_drop_time) / diff_drop_time)
if diff_dropped < 50:
diff_dropped = 50
if real_diff >= int(diff_dropped):
bok = True
return bok
def verify_diff(db):
"""Function for verification of block hashes, starting from block 854660 when Bismuth Heavy was introduced
"""
with sqlite3.connect(db) as ledger_check:
ledger_check.text_factory = str
h3 = ledger_check.cursor()
h4 = ledger_check.cursor()
try:
print("---> Verification of diffs started...")
mining_open()
# verify blockchain
print_step = 854000
invalid = 0
db_block_hash_prev = ""
db_timestamp_prev = ""
for row in h3.execute('SELECT * FROM misc where block_height>854660 ORDER BY block_height'):
db_block_height = row[0]
db_diff = int(float(row[1]))
h4.execute("SELECT * FROM transactions WHERE block_height = {} and reward != 0".format(db_block_height))
result = h4.fetchall()[0]
db_timestamp = quantize_two(result[1])
miner_address = str(result[2])
db_block_hash = str(result[7])
db_nonce = str(result[11])
if len(db_block_hash_prev)>1:
bok = check_block(db_block_height,miner_address,db_nonce,db_block_hash_prev,db_diff,db_timestamp,
db_timestamp,db_timestamp_prev)
if not bok:
print("Diff mismatch: {}".format(db_block_height))
invalid = invalid + 1
if db_block_height > print_step:
print("Bismuth diffs, verified {}".format(print_step))
print_step += STEP
db_block_hash_prev = db_block_hash
db_timestamp_prev = db_timestamp
if invalid == 0:
print("All diffs in the local ledger are valid")
else:
print("{} invalid diffs found".format(invalid))
h3.close()
h4.close()
except Exception as e:
print("Error: {}".format(e))
raise
def sha256_file(filename):
"""Calculate sha256 hash of file
"""
buf_size = 65536 # lets read stuff in 64kb chunks!
sha256 = hashlib.sha256()
with open(filename, 'rb') as f:
while True:
data = f.read(buf_size)
if not data:
break
sha256.update(data)
return sha256.hexdigest()
def download_file(url, filename):
"""From node.py: Download a file from URL to filename
:param url: URL to download file from
:param filename: Filename to save downloaded data as
returns `filename`
"""
try:
r = requests.get(url, stream=True)
total_size = int(r.headers.get('content-length')) / 1024
with open(filename, 'wb') as filename:
chunkno = 0
for chunk in r.iter_content(chunk_size=1024):
if chunk:
chunkno = chunkno + 1
if chunkno % 5e4 == 0: # every x chunks
print("Downloaded {} %".format(int(100 * ((chunkno) / total_size))))
filename.write(chunk)
filename.flush()
print("Downloaded 100 %")
return filename
except:
raise
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Download recent Bismuth ledger.')
parser.add_argument('--snapshot', type=int,
help='select snapshot url number (1 or 2)')
args = parser.parse_args()
if not os.path.isfile("heavy3a.bin"):
create_heavy3a()
print("Checking sha256 of heavy3a.bin file")
hash = sha256_file("heavy3a.bin")
if hash != "ffe30d8a63e1731e613b16ff8fd040d2946dba6a09823d7cc09d837570c55199":
print("Invalid sha256 of file heavy3a.bin. Delete the file and try again.")
sys.exit()
url='https://hypernodes.bismuth.live/snapshots.json'
resp = requests.get(url=url)
data = resp.json()
print("Functions included from the Mining_Heavy3 module")
print("Select snapshot number (1-{}) and press ENTER:".format(len(data)))
i=1
for site in data:
print("[{}] Block_height={} url={}".format(i,site['block_height'],site['url']))
i=i+1
if args.snapshot is None:
j=input('Enter your input:')
j=int(j)-1
else:
print("Selected snapshot number {} by --snapshot argument".format(args.snapshot))
j=args.snapshot-1
if 0 <= j <len(data):
ledger = 'static/ledger.tar.gz'
download_file(data[j]['url'],ledger)
print("---> Checking file hash (sha256)")
file_hash = sha256_file(ledger)
if file_hash != data[j]['sha256']:
print('---> Incorrect file hash')
else:
print("---> Correct file hash")
print("---> Removing *.db-shm and *.db-wal")
purge(glob.glob('static/*.db-shm'))
purge(glob.glob('static/*.db-wal'))
print("---> Extracting tar file")
with tarfile.open(ledger) as tar:
tar.extractall("static/")
print("---> Verifying block hashes")
db_hash = hash_blocks_until('static/ledger.db',DB_START)
if db_hash != DB_HASH:
print('---> Incorrect db hash')
else:
print('Correct db hash')
verify_blocks('static/ledger.db',DB_START)
print("---> Verifying mining difficulties")
verify_diff('static/ledger.db')
print("---> Looking for duplicate signatures")
bok = check_dupes('static/ledger.db')
if bok:
print("No duplicate signatures found")
You can’t perform that action at this time.