Advertisement
Guest User

miner_opt.py

a guest
May 22nd, 2017
855
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.81 KB | None | 0 0
  1. #
  2. # miner_opt.py v0.1, to be used with Python2
  3. #
  4. # Optimized CPU-miner by Primedigger for the Bismuth cryptocurrency, see https://github.com/hclivess/Bismuth
  5. #
  6. # Bitcointalk thread: https://bitcointalk.org/index.php?topic=1896497.0 (main)
  7. # https://bitcointalk.org/index.php?topic=1898984.120 (buy / sell Bismuth)
  8. #
  9. #
  10. # Based on the observation that only a small part of the time is spend calculating sha224 hashes in the reference miner
  11. # This is mostly due to "def bin_convert" being quite expensive in Python (the function is named bin_convert_orig in this implementation)
  12. # and also the SQL commands for calculating the diff taking a long time
  13. #
  14. #
  15.  
  16. import math, base64, sqlite3, os, hashlib, time, socks, keys, log
  17. from Crypto.Signature import PKCS1_v1_5
  18. from Crypto.Hash import SHA
  19. from Crypto import Random
  20. from multiprocessing import Process, freeze_support
  21.  
  22. # After how many cycles to print speed message. In this miner version, 1 cycle = num_block_hashes
  23. debug_print_mod = 10
  24.  
  25. # num_block_hashes specifies how many iterations nonce should be increased by +1
  26. # before diff and the random part of the nonce is recaluated.
  27. # This number can be tuned. It shouldn't be too high, otherwise you miss out on diff changes.
  28. num_block_hashes = 10000
  29.  
  30. # load config
  31. lines = [line.rstrip('\n') for line in open('config.txt')]
  32. for line in lines:
  33.     if "port=" in line:
  34.         port = line.strip('port=')
  35.     if "mining_ip=" in line:
  36.         mining_ip_conf = line.strip("mining_ip=")
  37.     if "mining_threads=" in line:
  38.         mining_threads_conf = line.strip('mining_threads=')
  39.     if "diff_recalc=" in line:
  40.         diff_recalc_conf = line.strip('diff_recalc=')
  41.     if "tor=" in line:
  42.         tor_conf = int(line.strip('tor='))
  43.     if "miner_sync=" in line:
  44.         sync_conf = int(line.strip('miner_sync='))
  45.     if "debug_level=" in line:
  46.         debug_level_conf = line.strip('debug_level=')
  47. # load config
  48.  
  49. def check_uptodate(interval, app_log):
  50.     # check if blocks are up to date
  51.     while sync_conf == 1:
  52.         conn = sqlite3.connect("static/ledger.db")  # open to select the last tx to create a new hash from
  53.         conn.text_factory = str
  54.         c = conn.cursor()
  55.  
  56.         execute(c, ("SELECT timestamp FROM transactions WHERE reward != 0 ORDER BY block_height DESC LIMIT 1;"), app_log)
  57.         timestamp_last_block = c.fetchone()[0]
  58.         time_now = str(time.time())
  59.         last_block_ago = float(time_now) - float(timestamp_last_block)
  60.  
  61.         if last_block_ago > interval:
  62.             app_log.warning("Local blockchain is {} minutes behind ({} seconds), waiting for sync to complete".format(int(last_block_ago) / 60,last_block_ago))
  63.             time.sleep(5)
  64.         else:
  65.             break
  66.         conn.close()
  67.     # check if blocks are up to date
  68.  
  69. def send(sdef, data):
  70.     sdef.sendall(data)
  71.  
  72. bin_format_dict = dict((x,format(ord(x), 'b')) for x in '0123456789abcdef')
  73.  
  74. def bin_convert(string):
  75.     return ''.join(bin_format_dict[x] for x in string)
  76.  
  77. def bin_convert_orig(string):
  78.     return ''.join(format(ord(x), 'b') for x in string)
  79.  
  80. def execute(cursor, what, app_log):
  81.     # secure execute for slow nodes
  82.     passed = 0
  83.     while passed == 0:
  84.         try:
  85.             # print cursor
  86.             # print what
  87.  
  88.             cursor.execute(what)
  89.             passed = 1
  90.         except Exception, e:
  91.             app_log.warning("Retrying database execute due to {}".format(e))
  92.             time.sleep(0.1)
  93.             pass
  94.             # secure execute for slow nodes
  95.     return cursor
  96.  
  97. def execute_param(cursor, what, param, app_log):
  98.     # secure execute for slow nodes
  99.     passed = 0
  100.     while passed == 0:
  101.         try:
  102.             # print cursor
  103.             # print what
  104.             cursor.execute(what, param)
  105.             passed = 1
  106.         except Exception, e:
  107.             app_log.warning("Retrying database execute due to {}".format(e))
  108.             time.sleep(0.1)
  109.             pass
  110.             # secure execute for slow nodes
  111.     return cursor
  112.  
  113. def miner(q,privatekey_readable, public_key_hashed, address):
  114.     from Crypto.PublicKey import RSA
  115.     Random.atfork()
  116.     key = RSA.importKey(privatekey_readable)
  117.     app_log = log.log("miner_"+q+".log",debug_level_conf)
  118.     rndfile = Random.new()
  119.     tries = 0
  120.  
  121.     while True:
  122.         try:
  123.             tries = tries +1
  124.             start_time = time.time()
  125.  
  126.             #
  127.             # You can also remove the 1==1 and do the diff recalcuation ever so often (with tries % x).
  128.             # But you might miss out on diff changes and produce a wrong block with a wrong diff
  129.             #
  130.  
  131.             if 1==1: #tries % 2 == 0 or tries == 1: #only do this ever so often
  132.                 block_timestamp = '%.2f' % time.time()
  133.  
  134.                 conn = sqlite3.connect("static/ledger.db") #open to select the last tx to create a new hash from
  135.                 conn.text_factory = str
  136.                 c = conn.cursor()
  137.                 execute(c ,("SELECT block_hash, timestamp FROM transactions WHERE reward != 0 ORDER BY block_height DESC LIMIT 1;"), app_log)
  138.                 result = c.fetchall()
  139.                 db_block_hash = result[0][0]
  140.                 timestamp_last_block = float(result[0][1])
  141.  
  142.                 # calculate difficulty
  143.                 execute_param(c, ("SELECT block_height FROM transactions WHERE CAST(timestamp AS INTEGER) > ? AND reward != 0"), (timestamp_last_block - 1800,), app_log)  # 1800=30 min
  144.                 blocks_per_30 = len(c.fetchall())
  145.  
  146.                 diff = blocks_per_30 * 2
  147.  
  148.                 # drop diff per minute if over target
  149.                 time_drop = time.time()
  150.  
  151.                 drop_factor = 120  # drop 0,5 diff per minute
  152.  
  153.                 if time_drop > timestamp_last_block + 120:  # start dropping after 2 minutes
  154.                     diff = diff - (time_drop - timestamp_last_block) / drop_factor  # drop 0,5 diff per minute (1 per 2 minutes)
  155.  
  156.                 if time_drop > timestamp_last_block + 300 or diff < 37:  # 5 m lim
  157.                     diff = 37  # 5 m lim
  158.                         # drop diff per minute if over target
  159.  
  160.                 #app_log.warning("Mining, {} cycles passed in thread {}, difficulty: {}, {} blocks per minute".format(tries,q,diff,blocks_per_minute))
  161.                 diff = int(diff)
  162.  
  163.  
  164.             if tries % debug_print_mod == 0:
  165.                 print 'db_block_hash:', db_block_hash, 'diff:',diff
  166.            
  167.             nonce_acc_len = 8
  168.             nonce_acc_pf = "%." + str(nonce_acc_len) + "x"
  169.             nonce = hashlib.sha224(rndfile.read(16)+str(q)).hexdigest()[:32 - nonce_acc_len]
  170.             count = 0
  171.  
  172.             mining_condition_bin = bin_convert_orig(db_block_hash)[0:diff]
  173.  
  174.             mining_condition_test_bin = ''
  175.             diff_hex = 0
  176.             while(len(mining_condition_test_bin) < diff):
  177.                 diff_hex += 1
  178.                 mining_condition_test_bin = bin_convert(db_block_hash[0:diff_hex])
  179.             diff_hex -= 1
  180.  
  181.             mining_condition = db_block_hash[0:diff_hex]
  182.  
  183.             hash_time = 0.0
  184.  
  185.            
  186.  
  187.             # Compute the static part of the hash (this doesn't change if we change the nonce)
  188.             start_hash = hashlib.sha224()
  189.             start_hash.update(address)
  190.            
  191.             # efficiently scan nonces
  192.             for count in xrange(num_block_hashes):
  193.                 try_nonce = nonce + (nonce_acc_pf % count)
  194.  
  195.                 mining_hash_lib = start_hash.copy()
  196.                 mining_hash_lib.update(try_nonce + db_block_hash)
  197.                 #hash_stop_time = time.time()
  198.  
  199.                 #hash_time += hash_stop_time - hash_start_time
  200.  
  201.                 mining_hash = mining_hash_lib.hexdigest()  # hardfork
  202.                
  203.                 # we first check hex diff, then binary diff
  204.                 if mining_condition in mining_hash:
  205.                     if mining_condition_bin in bin_convert(mining_hash):
  206.                         #recheck
  207.                         mining_hash_check = hashlib.sha224(address + try_nonce + db_block_hash).hexdigest()
  208.                         if mining_hash_check != mining_hash or mining_condition_bin not in bin_convert_orig(mining_hash_check):
  209.                             print "FOUND block, but block hash doesn't match:", mining_hash_check, 'vs.', mining_hash
  210.                             break
  211.                         else:
  212.                             print "YAY FOUND BLOCK, CHECK CORRECT"
  213.                             print "NEW BLOCK HASH: ", mining_hash_check, "mining condition:", mining_condition
  214.  
  215.                         app_log.warning("Thread {} found a good block hash in {} cycles".format(q,tries))
  216.  
  217.                         # serialize txs
  218.                         mempool = sqlite3.connect("mempool.db")
  219.                         mempool.text_factory = str
  220.                         m = mempool.cursor()
  221.                         execute(m, ("SELECT * FROM transactions ORDER BY timestamp;"), app_log)
  222.                         result = m.fetchall()  # select all txs from mempool
  223.                         mempool.close()
  224.  
  225.                         #include data
  226.                         block_send = []
  227.                         del block_send[:]  # empty
  228.                         removal_signature = []
  229.                         del removal_signature[:]  # empty
  230.  
  231.                         for dbdata in result:
  232.                             transaction = (
  233.                                 str(dbdata[0]), str(dbdata[1][:56]), str(dbdata[2][:56]), '%.8f' % float(dbdata[3]), str(dbdata[4]), str(dbdata[5]), str(dbdata[6]),
  234.                                 str(dbdata[7]))  # create tuple
  235.                             # print transaction
  236.                             block_send.append(transaction)  # append tuple to list for each run
  237.                             removal_signature.append(str(dbdata[4]))  # for removal after successful mining
  238.  
  239.                         # claim reward
  240.                         transaction_reward = tuple
  241.                         transaction_reward = (str(block_timestamp), str(address[:56]), str(address[:56]), '%.8f' % float(0), "0", str(try_nonce))  # only this part is signed!
  242.                         # print transaction_reward
  243.  
  244.                         h = SHA.new(str(transaction_reward))
  245.                         signer = PKCS1_v1_5.new(key)
  246.                         signature = signer.sign(h)
  247.                         signature_enc = base64.b64encode(signature)
  248.  
  249.                         block_send.append((str(block_timestamp), str(address[:56]), str(address[:56]), '%.8f' % float(0), str(signature_enc),
  250.                                            str(public_key_hashed), "0", str(try_nonce)))  # mining reward tx
  251.                         # claim reward
  252.                         # include data
  253.  
  254.                         tries = 0
  255.  
  256.                         #submit mined block to node
  257.  
  258.                         if sync_conf == 1:
  259.                             check_uptodate(300, app_log)
  260.  
  261.                         submitted = 0
  262.                         while submitted == 0:
  263.                             try:
  264.                                 s = socks.socksocket()
  265.                                 s.connect((mining_ip_conf, int(port)))  # connect to local node
  266.                                 app_log.warning("Connected")
  267.  
  268.                                 app_log.warning("Miner: Proceeding to submit mined block")
  269.  
  270.                                 send(s, (str(len("block"))).zfill(10))
  271.                                 send(s, "block")
  272.                                 send(s, (str(len(str(block_send)))).zfill(10))
  273.                                 send(s, str(block_send))
  274.  
  275.  
  276.                                 submitted = 1
  277.                                 app_log.warning("Miner: Block submitted")
  278.  
  279.                             except Exception, e:
  280.                                 print e
  281.                                 app_log.warning("Miner: Please start your node for the block to be submitted or adjust mining ip in settings.")
  282.                                 time.sleep(1)
  283.  
  284.                         #remove sent from mempool
  285.                         mempool = sqlite3.connect("mempool.db")
  286.                         mempool.text_factory = str
  287.                         m = mempool.cursor()
  288.                         for x in removal_signature:
  289.                             execute_param(m,("DELETE FROM transactions WHERE signature =?;"),(x,), app_log)
  290.                             app_log.warning("Removed a transaction with the following signature from mempool: {}".format(x))
  291.                         mempool.commit()
  292.                         mempool.close()
  293.  
  294.                 count += 1
  295.             stop_time = time.time()
  296.             time_diff = stop_time - start_time
  297.  
  298.  
  299.             if tries % debug_print_mod == 0:
  300.                 print time_diff, ' for '+str(num_block_hashes)+' hashes', float(num_block_hashes) / float(time_diff) , ' hashes per sec in thread', q
  301.  
  302.                 #remove sent from mempool
  303.  
  304.             #submit mined block to node
  305.  
  306.                 #break
  307.         except Exception, e:
  308.             print e
  309.             time.sleep(0.1)
  310.             raise
  311.  
  312. if __name__ == '__main__':
  313.     freeze_support()  # must be this line, dont move ahead
  314.  
  315.     app_log = log.log("miner.log",debug_level_conf)
  316.     (key, private_key_readable, public_key_readable, public_key_hashed, address) = keys.read()
  317.  
  318.     if not os.path.exists('mempool.db'):
  319.         # create empty mempool
  320.         mempool = sqlite3.connect('mempool.db')
  321.         mempool.text_factory = str
  322.         m = mempool.cursor()
  323.         execute(m,("CREATE TABLE IF NOT EXISTS transactions (timestamp, address, recipient, amount, signature, public_key, openfield)"), app_log)
  324.         mempool.commit()
  325.         mempool.close()
  326.         app_log.warning("Core: Created mempool file")
  327.         # create empty mempool
  328.     else:
  329.         app_log.warning("Mempool exists")
  330.  
  331.     # verify connection
  332.     connected = 0
  333.     while connected == 0:
  334.         try:
  335.             s = socks.socksocket()
  336.             if tor_conf == 1:
  337.                 s.setproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
  338.             s.connect((mining_ip_conf, int(port)))
  339.             app_log.warning("Connected")
  340.             connected = 1
  341.             s.close()
  342.         except Exception, e:
  343.             print e
  344.             app_log.warning(
  345.                 "Miner: Please start your node for the block to be submitted or adjust mining ip in settings.")
  346.             time.sleep(1)
  347.     # verify connection
  348.     if sync_conf == 1:
  349.         check_uptodate(120, app_log)
  350.  
  351.     instances = range(int(mining_threads_conf))
  352.     print instances
  353.     for q in instances:
  354.         p = Process(target=miner,args=(str(q+1),private_key_readable, public_key_hashed, address))
  355.         p.daemon = True
  356.         p.start()
  357.         print "thread "+str(p)+ " started"
  358.     for q in instances:
  359.         p.join()
  360.         p.terminate()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement