Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
"""
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")