def test_validate_scrypt(self): """ confirm scrypt validation of difficulty works properly """ header_hex = ("01000000f615f7ce3b4fc6b8f61e8f89aedb1d0852507650533a9e3" "b10b9bbcc30639f279fcaa86746e1ef52d3edb3c4ad8259920d509b" "d073605c9bf1d59983752a6b06b817bb4ea78e011d012d59d4") header_bytes = header_hex.decode('hex') target = target_unpack(unhexlify("1d018ea7")) self.assertTrue(BlockTemplate.validate_scrypt(header_bytes, target))
def update_pool(conn): try: # request local memory pool and load it in bt = conn.getblocktemplate({'capabilities': [ 'coinbasevalue', 'coinbase/append', 'coinbase', 'generation', 'time', 'transactions/remove', 'prevblock', ]}) except Exception: logger.warn("Failed to fetch new job, RPC must be down..") down_connection(conn, net_state) return False dirty = 0 # track a change in the transaction pool for trans in bt['transactions']: if trans['hash'] not in net_state['transactions']: dirty += 1 new_trans = Transaction(unhexlify(trans['data']), fees=trans['fee']) assert trans['hash'] == new_trans.lehexhash net_state['transactions'][trans['hash']] = new_trans if dirty or len(net_state['jobs']) == 0: # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (config['extranonce_size'] + config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(bt['height'], b'\0' * extranonce_length)) # simple output to the proper address and value fees = 0 for t in net_state['transactions'].itervalues(): fees += t.fees coinbase.outputs.append( Output.to_address(bt['coinbasevalue'] - fees, config['pool_address'])) job_id = hexlify(pack(str("I"), net_state['job_counter'])) logger.info("Generating new block template with {} trans" .format(len(net_state['transactions']))) bt_obj = BlockTemplate.from_gbt(bt, coinbase, extranonce_length, []) bt_obj.job_id = job_id bt_obj.block_height = bt['height'] bt_obj.acc_shares = set() net_state['job_counter'] += 1 net_state['jobs'][job_id] = bt_obj net_state['latest_job'] = job_id logger.debug("Adding {} new transactions to transaction pool, " "created job {}".format(dirty, job_id)) return bt_obj
def test_block_header2(self): # pulled from litecoin blockchain and modded slightly with full block header block_data = { 'bits': '1d018ea7', 'hash': 'adf6e2e56df692822f5e064a8b6404a05d67cccd64bc90f57f65b46805e9a54b', 'height': 29255, 'merkleroot': '066b2a758399d5f19b5c6073d09b500d925982adc4b3edd352efe14667a8ca9f', 'nonce': hexlify(pack(str(">L"), 3562614017)), 'previousblockhash': '279f6330ccbbb9103b9e3a5350765052081ddbae898f1ef6b8c64f3bcef715f6', 'curtime': 1320884152, 'raw_header': '01000000f615f7ce3b4fc6b8f61e8f89aedb1d0852507650533a9e3b10b9bbcc30639f279fcaa86746e1ef52d3edb3c4ad8259920d509bd073605c9bf1d59983752a6b06b817bb4ea78e011d012d59d4', 'tx': [('066b2a758399d5f19b5c6073d09b500d925982adc4b3edd352efe14667a8ca9f', '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804b217bb4e022309ffffffff0100f2052a010000004341044870341873accab7600d65e204bb4ae47c43d20c562ebfbf70cbcb188da98dec8b5ccf0526c8e4d954c6b47b898cc30adf1ff77c2e518ddc9785b87ccb90b8cdac00000000' )], 'version': 1 } # make a list of objects # confirm that the objects hash correctly coinbase = None transactions = [ Transaction(unhexlify(data.encode('ascii')), disassemble=True) for _, data in block_data['tx'] ] self.assertEquals(hexlify(merkleroot(transactions, be=True)[0]), block_data['merkleroot']) for obj, hsh in zip(transactions, block_data['tx']): hsh = hsh[0] obj.disassemble() self.assertEquals(obj.lehexhash, hsh) if obj.is_coinbase: idx = transactions.index(obj) coinbase = transactions.pop(idx) print("Found coinbase idx {} Amount is {}".format( idx, coinbase.outputs[0].amount)) pprint(coinbase.to_dict()) tmplt = BlockTemplate.from_gbt(block_data, coinbase, transactions=transactions) self.assertEquals(hexlify(tmplt.merkleroot_be(coinbase)), block_data['merkleroot']) header = tmplt.block_header(block_data['nonce'], b'', b'') self.assertEquals(block_data['raw_header'], hexlify(header)) assert tmplt.validate_scrypt( header, target_unpack(unhexlify(block_data['bits'])))
def test_block_template(): gbt = {u'coinbaseaux': {u'flags': u''}, u'vbavailable': {}, u'previousblockhash': u'00000061786317587bcfe97516cd00a541962334779d60efdb7c4efeb670ac77', u'target': u'7fffff0000000000000000000000000000000000000000000000000000000000', u'noncerange': u'00000000ffffffff', u'transactions': [], u'rules': [], u'vbrequired': 0, u'curtime': 1500584586, u'capabilities': [u'proposal'], u'height': 16, 'update_time': 1500584586.899153, u'mintime': 1500584329, u'version': 536870912, u'bits': u'207fffff', u'coinbasevalue': 5000000000, u'sigoplimit': 20000, u'sizelimit': 1000000, u'mutable': [u'time', u'transactions', u'prevblock'], u'longpollid': u'00000061786317587bcfe97516cd00a541962334779d60efdb7c4efeb670ac7718'} extranonce_length = 4 coinbase = Transaction() coinbase.version = 2 coinbase.inputs.append( Input.coinbase(gbt['height'], [], extra_script_sig=b'\0' * extranonce_length)) coinbase_value = 10000000 coinbase.outputs.append(Output.to_address(coinbase_value, "1F1tAaz5x1HUXrCNLbtMDqcw6o5GNn4xqX")) bt_obj = BlockTemplate.from_gbt(gbt, coinbase, extranonce_length, [])
def push_job(self, job_data, id, flush=False, push=False): new_job = BlockTemplate() new_job.__dict__.update(job_data) if push: for idx, client in viewitems(self.stratum_manager.clients): try: if flush: client.new_block_event.set() else: client.new_work_event.set() except AttributeError: pass
def test_stratum_confirm(self): """ Test some raw data from cgminer submitting a share, confirm hashes come out the same as cgminer. Raw stratum params: """ gbt = {u'bits': u'1e00e92b', u'coinbaseaux': {u'flags': u'062f503253482f'}, u'coinbasevalue': 5000000000, u'curtime': 1392509565, u'height': 203588, u'mintime': 1392508633, u'mutable': [u'time', u'transactions', u'prevblock'], u'noncerange': u'00000000ffffffff', u'previousblockhash': u'b0f5ecb62774f2f07fdc0f72fa0585ae3e8ca78ad8692209a355d12bc690fb73', u'sigoplimit': 20000, u'sizelimit': 1000000, u'target': u'000000e92b000000000000000000000000000000000000000000000000000000', u'transactions': [], u'version': 2} extra1 = '0000000000000000' submit = {'extra2': '00000000', 'nonce': 'd5160000', 'result': '000050ccfe8a3efe93b2ee33d2aecf4a60c809995c7dd19368a7d00c86880f30'} # build a block template object from the raw data coinbase = Transaction() coinbase.version = 2 coinbase.inputs.append(Input.coinbase(gbt['height'], b'\0' * 12)) coinbase.outputs.append(Output.to_address(gbt['coinbasevalue'], 'D7QJyeBNuwEqxsyVCLJi3pHs64uPdMDuBa')) transactions = [] for trans in gbt['transactions']: new_trans = Transaction(unhexlify(trans['data']), fees=trans['fee']) assert trans['hash'] == new_trans.lehexhash transactions.append(new_trans) bt = BlockTemplate.from_gbt(gbt, coinbase, 12, transactions) send_params = bt.stratum_params() print("job_id: {0}\nprevhash: {1}\ncoinbase1: {2}\ncoinbase2: {3}" "\nmerkle_branch: {4}\nversion: {5}\nnbits: {6}\nntime: {7}" .format(*send_params)) header = bt.block_header(submit['nonce'], extra1, submit['extra2']) hash_bin = scrypt(header) target = target_from_diff(1, 0x0000FFFF00000000000000000000000000000000000000000000000000000000) hash_int = uint256_from_str(hash_bin) hash_hex = "%064x" % hash_int self.assertEquals(hash_hex, submit['result']) assert hash_int < target
def test_block_header(self): return # pulled from dogecoin blockchain and modded slightly, height 50000 block_data = { 'bits': '1c00c7ec', 'hash': 'e4cab588a33147a217c8bc2f923fcd1f642fde26c7c797a5c2c4808c4c617a7e', 'height': 50000, 'merkleroot': '5af19843e6220965f3c4b5f8d3995796edff0aad5ee4cc6ab849333881c001e1', 'nonce': hexlify(pack(str(">L"), 3014526208)), 'previousblockhash': '7f0502292609f5949260403176e8874ec7c5376397641d3f660648e7fdda0bab', 'curtime': 1389349309, 'tx': [ ('d4eeb0cd44a4339d1a9fe8cea9482689165cff6ed1a3f4815bafcb443cdd271e', '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff250350c300062f503253482f04bcc9cf5208f80032c0020000000b2f4e757432506f6f6c732f00000000010b54074f2f4b00001976a9141b3fdba0ff497a9d8e84d65e9b078e23e46e97f088ac00000000'), ('481fce0e2572dd782369dcc2457c267bb70918e825dfedd6e178ddf28d016cde', '0100000006ca0667d0021af9199f134a38ece3f813200f457282c2904d11b5c14a773ad514010000006b48304502200470b7b254b1d4b75f3318c6348ac97e2ba35f4be838aa10b50429107b4a8026022100864687886781c4a16de03b18fc5236bd379a6655f075b75da308b9bc3d7565b801210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff9f9316e06ba91ceb8d93a3f3b1286570ccc9f5720c944a556e37a11a0789d99a010000006b483045022100fc72dadfb07f7253b948bd94c3e9e534a4f45fdd70d6f25e865afc079f5f07ac022028be9ec4d68e700f5843a72e387f1a2bdf0cacb06f19ad8fbbc3db15ceaadd9401210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffffcc803949dfe10ac0d9b7adc4b560a52fdbfc3fbf5a2cd4c515bdc1ccee1919e6010000006a4730440220385fb14c3f6559557c6ff0a1beab9c0a3a1f3ee7b5c466874566f25063be13fb022001b84680899638bd99fcea1f8a007ccda4df954a594210a1e6b2644c681b56b601210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff340b8f285ceea5a123aaae8d33fd940442962894b8276a0f1d0959072c85b1e2010000006b4830450221008c592892fb530499f75065d4c741a97bb89e7aab7f156cf6471e134e41d19b3802202355e2f57c8ab27eb9445612e18f8ca0c51d0c8bad035082f784157db006419501210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff57835281cd613c9afc81e339d4d6b5ff20e25b381015b99ad863c3cec9c456d3010000006b48304502201ebdf69c6e146dc9f832d32439a1fef26011eecac1ffdc246ed37b726ca33f9a022100bb4e8f82c2c89c254700196f08e277af6964a04122a6395d42e61ffdbbb08ac601210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff5d22d9f2819f918f92bfb529c58201ed382a38d67a20c4277d104ecd92cb84c1010000006c493046022100834cfab229487cef0fe22dd07bbcf83bd3ce2696aee904b906c0cd8791e86a74022100a9dc16328ca8c648a0999d584fb1003fd4375aabe05d273143e976a735cf72f001210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff010011133a4f0200001976a914db4d7ba9e58109182b04307cd587fc0c79f659e788ac00000000'), ('74c60a18fc9a265cf78973d36e20b781ae427d4d62e9818bc51de000ef6fb1e3', '0100000006ec24cf82341457fe1f4e5a8aa70cd0cd24577d55de00f0220687edb5c4b18f48000000006b483045022100ca65846ad4fddc08b6e36853f6d0175df6a967c0c76c2804f07e3efe4a07901002207b67d8a7ee76dcf45f9515589381cccd2db33cb602b85f3f1abc873bfd2bb0680121037c533b95e310e28de6ea11179dac58b5a44e6de5eb37aacdfcb33c7d3c241cdaffffffffdf50b75ca17711f4e7060684389eaba1f9b928e160021ceb5348d338ecc827fa010000006a47304402202eca6850b9c25e8974441dd386b58d9a615ab8527f46890d10861916ee25b87f02201d35044b1ec20d6d2ec2e292354acc5d65ae8be10bf57cbd17d51ce0ac07dd6c012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150fffffffff6157a915b05f2c5d823123fb4b230b045fdbff0f68578d44de49f78cf0ede5b010000006b48304502201b61e8486f039524fb5a2fd51fdc75c170502a4adc43fdfefb830abcf572b4d0022100b5f134301962e661e8251f79182344d879dbd990c0513b6ee96ad45ffb5b9475012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150ffffffffd48aed18cad37e0d0f5bbcdd1b7f0a56899bdae664164dab0e243be02761a9ba010000006b483045022100c0c06d4fb2ec9f473f3b5fe9e96ab1379f9431cc639256a780d395b8c816a801022049c38e24224e02c7d103dbb4ffe650f86a85efbbd06e09f14bb68289813a88ce012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150ffffffffbb1f79fb8ef08188bbe5720afc51e8d4b17b227ac0575e06b113e9aa2cf73c9a010000006b483045022100de8dfc418ca7e9cbc3b8cee601e06ded8519490c3e3a69ad445e4bdbae6646a50220675e874008150d6c407211588819d42640e873cd5124a6acedfc66c7f5aae088012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150ffffffffdc2dff24673739435ef472cedc633557f21c6909822a40136c4b75c08797f6f7010000006a473044022045c537da10375757cb53ec70c06c6f00011c0759bdf8e6df2a012ce33153c7e102206e1e35d6f57c7de5ad37272efdecaf7313ada0e8f66f9532dd0747b09cb8003e012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150ffffffff0231cc5893000000001976a914262fee33cefdf111c3515c0502ba69073609904888ac0038c566e40200001976a914bd007a5819157bc912364e54fe7e0551df06d54888ac00000000'), ('12127842a829a588be0ac25a3ab5505925e79440d6911d22dd1059c79b9966e7', '0100000002ac367cb4c2ebd51066b0a87bac57a0f4876a6be781b1d2935bb49d1320c735f7000000006b48304502207a58e61dbe5ab39e7f6b3a91bf62a33102dfc6e7d2270d0f2cd31d0fd2b58a4a022100f27ffec655cf27ce9d4facbc9ce1b079f0c7b3fe80db69111695825e23628209012102c9619b298f6272cd0710ff5997f38dfd39096e3c95b92f857da6f4843e4afbc7ffffffff6c7fc27f6debb16714276edf12868ab5deab5a0cd64c5152928a2f3798e1ee01000000006c493046022100895790720f307d12a41a6df11c83fe2c991a857e63048e74dde91fb672a368ea022100bf9dac5625b454ee38dba9fc7e8e911bf5dc8cf504709413a9b12adb9b77bc26012103dedeabe4b6166d97479db844ca2defbbb8c5394b7576aafd6be67175062dc4beffffffff01008fd3ac8b0000001976a91464e5cfa48bf5f22ce5a5e739667ef61afb0b192d88ac00000000'), ('48d5713b929e28f1810cc7effdbfb0cb11bc88068bfbb1c9f076536b794715af', '010000000501049ee2eba48bf43470d5a6c1328fc29e3f434cae2f92d6b55bc6b694083d4f010000006a4730440220656c1c8c96ac2e72451e1ac69e496ee3768cd35741a4605579a770bde17ebe8e02207b0093e79f4f9e99795575699fd38496b2b12ce1b994054505c52dc9092a2bba01210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffff17765b1d45fc0f0b0d9e060291bfaf19b9a219f3a74fb1fb6e20fda3278f252f010000006b4830450220264601cb87ca3579f4653248dfb539bd13bea41c63d378ab7ec86f5476ea99ab022100e5921d7d5b7c5421cf17a46729d1e08b26cfe3d64120d46a4c0faa623f91791301210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffff94a09224a810a12eb793cb87802f58e31e5fc1f38831be221a4d2f9910893dcb010000006c4930460221008160791119449e306c9db0b3f22dde248bbc73babd732052b26a58b29c86287b022100ccdd9cb9eb9d8791c45b691d09f5aba2bfd69f279edd9c8177942748a5075ed901210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffff9c9431729394c53656a625755dd2ff124a0d615932a2ea6befafd36812301ae0010000006a47304402202930eda28e865b8d190a305611473760df2b151175bc834155bc58435e82955102206eb77aceae4149715002ea4fee6eb2ffd807c1dc3fd37ea1d7eb28e88733883401210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffffae32166a760609d5fae1361f568977fb23e108a480a328298f075603a2bd124d010000006b48304502210094586a38aaa22ac5f1cb85871236057ae2fb8412fe5128768f2435eeadff0d8402202bd0000b2ddad2f1da83cfec60513aea0724f477bb55d5150e0de31955710c0d01210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffff0100bccfcd5a0000001976a91442f319a15fd608b67ddddcc2e3badf1b90554fe088ac00000000'), ('212499f6527467b49cdf946524b42266ca8751616a41b7181d3a8931da7b240e', '01000000018fa554391f4e45ae96b9c6e9bd35f1cf38737ee9ad67ba401329df7497625513000000006b483045022019331f164d059057e5dc12bab158678ba65faf1b9542811434ce13e1179550b4022100e2375b7f350b18c1bde49c8ddd7d245282b931e1a8204018bb01b8abff94f4790121038f63d5c111706674f50f3d28729fe9848e9a3aee0b965c674d14e2155c5ee35bffffffff0200a3af46bb0000001976a9149cc45c2d78927f22ab4008a907becf6ab642258288aca019f807000000001976a9144d4952746b2777f3d07a89ad0ffd02e2e1d9aed288ac00000000'), ('47bce257235ab0f6c8526a12fc23e3bec10bbd1d87a3a261f624ff5c0d2730f2', '0100000001a16eaa30e2d56f3a3a94bf4b640c2ffaa3ccd09ce3926567b2e63e3f4d55df26000000006b48304502203579c6afe9b7365252c0424aa5e3b9d00c850f2beee0c0b2f66ac7652c4eb002022100ca44a4a0e7d2ff81de117efd144a89c70acc87eaf2e3454fed175f0ef9b6f562012103f5f17ee5b476803cf332d56c293ce59aa77a5046a284c9f7a523fefd911e9980ffffffff0260119d0a010000001976a9149e027ca13765f6558fcbab6acec2fc561fc5dd3288ac9ec1764b010000001976a914957acdecb251260da54dc8510497cb761db0698c88ac00000000'), ('6b74e1534ee50fc7158593831e322717fa536d65cbeb03fe53b30b31853fa033', '010000000153323c2dbba06bc58227354b013d31ba01ba814fa5681fb211c039979cac1643000000006b483045022054f7930a06af7da2a3e35c3fbb9bb0fbf5897c9296286de46d96763ea8c1d8fc0221009b8d53d5964a57fc8469ece1b0ef6f43e2cdbe1d4d72308d04491cb4705f95ce012103bbd11e6843ebcbcd2024265332738d8e1ebc7d0bb4b3a2e5b7a8bd6ece14bcb7ffffffff029feec0e4000000001976a91481d7f7a63b4a4e236b3b5d43067212e52bcd25ae88acf73d7520000000001976a91447cd32e2669d30d86d1c6708b890b9d7c7b632f188ac00000000')], 'version': 1} # make a list of objects # confirm that the objects hash correctly coinbase = None transactions = [Transaction(unhexlify(data.encode('ascii')), disassemble=True) for _, data in block_data['tx']] self.assertEquals(hexlify(merkleroot(transactions)[0]), block_data['merkleroot']) for obj, hsh in zip(transactions, block_data['tx']): hsh = hsh[0] obj.disassemble() self.assertEquals(obj.lehexhash, hsh) if obj.is_coinbase: idx = transactions.index(obj) coinbase = transactions.pop(idx) print("Found coinbase idx {} Amount is {}" .format(idx, coinbase.outputs[0].amount)) tmplt = BlockTemplate.from_gbt(block_data, coinbase, transactions=transactions) self.assertEquals(hexlify(tmplt.merkleroot(coinbase)), block_data['merkleroot']) header = tmplt.block_header(block_data['nonce'], b'', b'') assert tmplt.validate_scrypt(header, target_unpack(unhexlify(block_data['bits'])))
def test_block_header4(self): # pulled from mozzshare blockchain and modded slightly with full block header block_data = { 'bits': '1b65bf06', 'hash': '00000000005f53a4158ec81a7f936875b117add6ddf16052be69db814ed08e5d', 'height': 95477, 'merkleroot': '68214acafcc87de6c6bf0389cf6fbaf85ce602972e188a422320260f28e57adc', 'nonce': hexlify(pack(str(">L"), 12348287)), 'previousblockhash': '00000000003fbdcb0529f2df6b9e846092c25fd472117f2dade884be824a519f', 'curtime': 1412981134, "reward": 120, 'tx': [ ('273de181f48d64299a81b16705fc0d7d9ee392e258af96a1e10a9dffce13d64c', '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2703f57401062f503253482f048661385408f80031f9dc0100000d2f7374726174756d506f6f6c2f0000000001408544cb020000001976a9145944e74d6aaf83be927703ad95e7308787bb55c588ac00000000'), ('d4fb5de6290873a66171d167ae91cec59e116e23c2a88b50741671a76061176e', '0100000001fccce331a39a2b923ee88c7660e0803214f9a677314f21c7cb3a81e25eeb9158010000006c493046022100c13d4e43a267fa6e3b74d559eb985e886d8efe6d7f78c6105d5f6e46677e9c0d02210096e2acfc2e745286c0e948cd3f085a2da6d396277644669f448699a280bcc955012102dfaf5dfed2eec785ed262b574b2cf1c2b5e613ca5149b24725b54cb62c8e94acffffffff02f2f1b507000000001976a914ea76974eccf823d94a3615c1a0a117dec16533d888ac1ac96ae4010000001976a914e2c6ffbd9a5187e474d8278f4d2ad4f2adad17cf88ac00000000'), ('2daaa24179c2bf1c95da24f8ea3bf4d2722326c03f6407676b23edb42d0c908d', '010000000154f1cf0ee5b7efd7e3f3d3e1b81d98c743c0b1ab8257a82119ac6d0e73362876000000006b4830450220153fb11ac82d3d97addb82e717d5cc95c0d4c31dc709d4f634f82d5c93bb2730022100b12f8251a57238bbaa5c1e6c6552c1d64671a0130a24afcffad723b6fe0f8bfc0121024a192ab361b42cb0aab26e877423b4c51402d98423912f5faad243a301285e9dffffffff02ca154407000000001976a914b652ec06255ecd4708f416e8ae17646b773d09e088acef47e09c010000001976a9149212ca9f86bc8f699f31be024d092963661a0eff88ac00000000'), ], 'version': 325781176} # make a list of objects # confirm that the objects hash correctly coinbase = None transactions = [Transaction(unhexlify(data.encode('ascii')), disassemble=True, coin='MLS') for _, data in block_data['tx']] self.assertEquals(hexlify(merkleroot(transactions, be=True, hash_func=hvc_hash)[0]).decode('ascii'), block_data['merkleroot']) for obj, hsh in zip(transactions, block_data['tx']): hsh = hsh[0] obj.disassemble() self.assertEquals(obj.behexhash, hsh) if obj.is_coinbase: idx = transactions.index(obj) coinbase = transactions.pop(idx) print("Found coinbase idx {} Amount is {}" .format(idx, coinbase.outputs[0].amount)) pprint(coinbase.to_dict()) tmplt = BlockTemplate.from_gbt(block_data, coinbase, transactions=transactions, coin='MLS') self.assertEquals(hexlify(tmplt.merkleroot_be(coinbase)).decode('ascii'), block_data['merkleroot']) header = tmplt.block_header(block_data['nonce'], b'', b'') self.assertEquals(block_data['hash'], hexlify(hvc_powhash(header)[::-1]).decode('ascii'))
def test_block_header2(self): # pulled from litecoin blockchain and modded slightly with full block header block_data = { 'bits': '1d018ea7', 'hash': 'adf6e2e56df692822f5e064a8b6404a05d67cccd64bc90f57f65b46805e9a54b', 'height': 29255, 'merkleroot': '066b2a758399d5f19b5c6073d09b500d925982adc4b3edd352efe14667a8ca9f', 'nonce': hexlify(pack(str(">L"), 3562614017)), 'previousblockhash': '279f6330ccbbb9103b9e3a5350765052081ddbae898f1ef6b8c64f3bcef715f6', 'curtime': 1320884152, 'raw_header': '01000000f615f7ce3b4fc6b8f61e8f89aedb1d0852507650533a9e3b10b9bbcc30639f279fcaa86746e1ef52d3edb3c4ad8259920d509bd073605c9bf1d59983752a6b06b817bb4ea78e011d012d59d4', 'tx': [ ('066b2a758399d5f19b5c6073d09b500d925982adc4b3edd352efe14667a8ca9f', '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804b217bb4e022309ffffffff0100f2052a010000004341044870341873accab7600d65e204bb4ae47c43d20c562ebfbf70cbcb188da98dec8b5ccf0526c8e4d954c6b47b898cc30adf1ff77c2e518ddc9785b87ccb90b8cdac00000000')], 'version': 1} # make a list of objects # confirm that the objects hash correctly coinbase = None transactions = [Transaction(unhexlify(data.encode('ascii')), disassemble=True) for _, data in block_data['tx']] self.assertEquals(hexlify(merkleroot(transactions, be=True)[0]), block_data['merkleroot']) for obj, hsh in zip(transactions, block_data['tx']): hsh = hsh[0] obj.disassemble() self.assertEquals(obj.lehexhash, hsh) if obj.is_coinbase: idx = transactions.index(obj) coinbase = transactions.pop(idx) print("Found coinbase idx {} Amount is {}" .format(idx, coinbase.outputs[0].amount)) pprint(coinbase.to_dict()) tmplt = BlockTemplate.from_gbt(block_data, coinbase, transactions=transactions) self.assertEquals(hexlify(tmplt.merkleroot_be(coinbase)), block_data['merkleroot']) header = tmplt.block_header(block_data['nonce'], b'', b'') self.assertEquals(block_data['raw_header'], hexlify(header)) assert tmplt.validate_scrypt(header, target_unpack(unhexlify(block_data['bits'])))
def test_block_header3(self): # pulled from heavycoin blockchain and modded slightly with full block header block_data = { 'bits': '1c040773', 'hash': '000000000035ef615d0708b7409021bde4e29ab063f7f8b531197d8230a931ca', 'height': 122588, 'merkleroot': '105faebb2ac14858bbf92d1ebd29dd49b5fd532a54097bc76818e3668dd6925b', 'nonce': hexlify(pack(str(">L"), 825226627)), 'previousblockhash': '0000000003d8d235668c6d41389dd703b828f4a0f02d30a37e62ca10ab144b3f', 'curtime': 1409012334, "vote" : 1, "reward" : 434, 'tx': [ ('105faebb2ac14858bbf92d1ebd29dd49b5fd532a54097bc76818e3668dd6925b', '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1103dcde010453fbd26e7b00000b7b000000ffffffff010072d71a0a0000001976a914caf784fd449b3cdde48c7542640f14b34aed42fd88ac00000000')], 'version': 350470948} # make a list of objects # confirm that the objects hash correctly coinbase = None transactions = [Transaction(unhexlify(data.encode('ascii')), disassemble=True, coin='HVC') for _, data in block_data['tx']] self.assertEquals(hexlify(merkleroot(transactions, be=True, hash_func=hvc_hash)[0]).decode('ascii'), block_data['merkleroot']) for obj, hsh in zip(transactions, block_data['tx']): hsh = hsh[0] obj.disassemble() self.assertEquals(obj.behexhash, hsh) if obj.is_coinbase: idx = transactions.index(obj) coinbase = transactions.pop(idx) print("Found coinbase idx {} Amount is {}" .format(idx, coinbase.outputs[0].amount)) pprint(coinbase.to_dict()) tmplt = BlockTemplate.from_gbt(block_data, coinbase, transactions=transactions, coin='HVC') self.assertEquals(hexlify(tmplt.merkleroot_be(coinbase)).decode('ascii'), block_data['merkleroot']) header = tmplt.block_header(block_data['nonce'], b'', b'', nvote=hexlify(pack(str(">H"), block_data['vote']))) self.assertEquals(block_data['hash'], hexlify(hvc_powhash(header)[::-1]).decode('ascii'))
def generate_job(self, push=False, flush=False, new_block=False): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if self.last_gbt is None: return merged_work = self.net_state['merged_work'] if self.net_state['merged_work']: tree, size = bitcoin_data.make_auxpow_tree(merged_work) mm_hashes = [ merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size) ] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack( dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) mm_later = [(aux_work, mm_hashes.index(aux_work['hash']), mm_hashes) for chain_id, aux_work in merged_work.iteritems()] else: mm_later = [] mm_data = None # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.config['extranonce_size'] + self.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self.last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(self.last_gbt['coinbasevalue'], self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self.net_state['job_counter'])) logger.info( "Generating new block template with {} trans. Diff {}. Subsidy {}." .format(len(self.last_gbt['transactions']), bits_to_difficulty(self.last_gbt['bits']), self.last_gbt['coinbasevalue'])) bt_obj = BlockTemplate.from_gbt( self.last_gbt, coinbase, extranonce_length, [ Transaction(unhexlify(t['data']), fees=t['fee']) for t in self.last_gbt['transactions'] ]) bt_obj.mm_later = copy(mm_later) hashes = [bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.job_id = job_id bt_obj.block_height = self.last_gbt['height'] bt_obj.acc_shares = set() if push: if flush: logger.info("New work announced! Wiping previous jobs...") self.net_state['jobs'].clear() self.net_state['latest_job'] = None else: logger.info("New work announced!") self.net_state['job_counter'] += 1 self.net_state['jobs'][job_id] = bt_obj self.net_state['latest_job'] = job_id if push: for idx, client in viewitems(self.stratum_clients): try: if flush: client.new_block_event.set() else: client.new_work_event.set() except AttributeError: pass if new_block: hex_bits = hexlify(bt_obj.bits) self.net_state['work']['difficulty'] = bits_to_difficulty(hex_bits) if self.config['send_new_block']: self.celery.send_task_pp('new_block', bt_obj.block_height, hex_bits, bt_obj.total_value)
def generate_job(self, push=False, flush=False, new_block=False, network='main'): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if not self._last_gbt: self.logger.warn("Cannot generate new job, missing last GBT info") return if self.auxmons: merged_work = {} auxdata = {} for auxmon in self.auxmons: # If this network hasn't pushed a job yet, skip it if auxmon.last_work['hash'] is None: continue merged_work[auxmon.last_work['chainid']] = dict( hash=auxmon.last_work['hash'], target=auxmon.last_work['type']) tree, size = bitcoin_data.make_auxpow_tree(merged_work) mm_hashes = [ merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size) ] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack( dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) for auxmon in self.auxmons: if auxmon.last_work['hash'] is None: continue data = dict(target=auxmon.last_work['target'], hash=auxmon.last_work['hash'], height=auxmon.last_work['height'], found_block=auxmon.found_block, index=mm_hashes.index(auxmon.last_work['hash']), type=auxmon.last_work['type'], hashes=mm_hashes) auxdata[auxmon.config['currency']] = data else: auxdata = {} mm_data = None # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.manager.config['extranonce_size'] + self.manager.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self._last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) coinbase_value = self._last_gbt['coinbasevalue'] # Payout Darkcoin masternodes mn_enforcement = self._last_gbt.get('enforce_masternode_payments', True) if (self.config['payout_drk_mn'] is True or mn_enforcement is True) \ and self._last_gbt.get('payee', '') != '': # Grab the darkcoin payout amount, default to 20% payout = self._last_gbt.get('payee_amount', coinbase_value / 5) coinbase_value -= payout coinbase.outputs.append( Output.to_address(payout, self._last_gbt['payee'])) self.logger.debug( "Created TX output for masternode at ({}:{}). Coinbase value " "reduced to {}".format(self._last_gbt['payee'], payout, coinbase_value)) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(coinbase_value, self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self._job_counter)) bt_obj = BlockTemplate.from_gbt( self._last_gbt, coinbase, extranonce_length, [ Transaction(unhexlify(t['data']), fees=t['fee']) for t in self._last_gbt['transactions'] ]) # add in our merged mining data if mm_data: hashes = [ bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions ] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.merged_data = auxdata bt_obj.job_id = job_id bt_obj.diff1 = self.config['diff1'] bt_obj.algo = self.config['algo'] bt_obj.currency = self.config['currency'] bt_obj.pow_block_hash = self.config['pow_block_hash'] bt_obj.block_height = self._last_gbt['height'] bt_obj.acc_shares = set() if flush: bt_obj.type = 0 elif push: bt_obj.type = 1 else: bt_obj.type = 2 bt_obj.found_block = self.found_block # Push the fresh job to users after updating details self._job_counter += 1 if flush: self.jobs.clear() self.jobs[job_id] = bt_obj self.latest_job = bt_obj self.new_job.job = bt_obj self.new_job.set() self.new_job.clear() event = ("{name}.jobmanager.new_job:1|c\n".format( name=self.manager.config['procname'])) if push or flush: self.logger.info( "{}: New block template with {:,} trans. " "Diff {:,.4f}. Subsidy {:,.2f}. Height {:,}. Merged: {}". format("FLUSH" if flush else "PUSH", len(self._last_gbt['transactions']), bits_to_difficulty(self._last_gbt['bits']), self._last_gbt['coinbasevalue'] / 100000000.0, self._last_gbt['height'], ', '.join(auxdata.keys()))) event += ("{name}.jobmanager.work_push:1|c\n".format( name=self.manager.config['procname'])) # Stats and notifications now that it's pushed if flush: event += ("{name}.jobmanager.work_restart:1|c\n".format( name=self.manager.config['procname'])) self.logger.info("New {} network block announced! Wiping previous" " jobs and pushing".format(network)) elif push: self.logger.info( "New {} network block announced, pushing new job!".format( network)) if new_block: hex_bits = hexlify(bt_obj.bits) self.current_net['difficulty'] = bits_to_difficulty(hex_bits) self.current_net['subsidy'] = bt_obj.total_value self.current_net['height'] = bt_obj.block_height - 1 self.current_net['last_block'] = time.time() self.current_net['prev_hash'] = bt_obj.hashprev_be_hex self.current_net['transactions'] = len(bt_obj.transactions) event += ("{name}.{curr}.difficulty:{diff}|g\n" "{name}.{curr}.subsidy:{subsidy}|g\n" "{name}.{curr}.job_generate:{t}|g\n" "{name}.{curr}.height:{height}|g".format( name=self.manager.config['procname'], curr=self.config['currency'], diff=self.current_net['difficulty'], subsidy=bt_obj.total_value, height=bt_obj.block_height - 1, t=(time.time() - self._last_gbt['update_time']) * 1000)) self.manager.log_event(event)
def generate_job(self, push=False, flush=False, new_block=False): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if not self._last_gbt: self.logger.warn("Cannot generate new job, missing last GBT info") return if self.merged_work: tree, size = bitcoin_data.make_auxpow_tree(self.merged_work) mm_hashes = [ self.merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size) ] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack( dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) merged_data = {} for aux_work in self.merged_work.itervalues(): data = dict(target=aux_work['target'], hash=aux_work['hash'], height=aux_work['height'], index=mm_hashes.index(aux_work['hash']), type=aux_work['type'], hashes=mm_hashes) merged_data[aux_work['type']] = data else: merged_data = {} mm_data = None self.logger.info("Generating new block template with {} trans. " "Diff {:,.4f}. Subsidy {:,.2f}. Height {:,}. " "Merged chains: {}".format( len(self._last_gbt['transactions']), bits_to_difficulty(self._last_gbt['bits']), self._last_gbt['coinbasevalue'] / 100000000.0, self._last_gbt['height'], ', '.join(merged_data.keys()))) # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.config['extranonce_size'] + self.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self._last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) # Darkcoin payee amount if self._last_gbt.get('payee', '') != '': payout = self._last_gbt['coinbasevalue'] / 5 self._last_gbt['coinbasevalue'] -= payout coinbase.outputs.append( Output.to_address(payout, self._last_gbt['payee'])) self.logger.info( "Paying out masternode at addr {}. Payout {}. Blockval reduced to {}" .format(self._last_gbt['payee'], payout, self._last_gbt['coinbasevalue'])) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(self._last_gbt['coinbasevalue'], self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self._job_counter)) bt_obj = BlockTemplate.from_gbt( self._last_gbt, coinbase, extranonce_length, [ Transaction(unhexlify(t['data']), fees=t['fee']) for t in self._last_gbt['transactions'] ]) # add in our merged mining data if mm_data: hashes = [ bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions ] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.merged_data = merged_data bt_obj.job_id = job_id bt_obj.diff1 = self.config['diff1'] bt_obj.algo = self.config['algo'] bt_obj.pow_block_hash = self.config['pow_block_hash'] bt_obj.block_height = self._last_gbt['height'] bt_obj.acc_shares = set() if push: if flush: self.logger.info("New work announced! Wiping previous jobs...") self.jobs.clear() self.latest_job = None else: self.logger.info("New work announced!") self._job_counter += 1 self.jobs[job_id] = bt_obj self.latest_job = job_id if push: t = time.time() bt_obj.stratum_string() for idx, client in viewitems(self.stratum_manager.clients): try: if client.authenticated: client._push(bt_obj, flush=flush) except AttributeError: pass self.logger.info( "New job enqueued for transmission to {} users in {}".format( len(self.stratum_manager.clients), time_format(time.time() - t))) if flush: self.server['work_restarts'].incr() self.server['work_pushes'].incr() self.server['new_jobs'].incr() if new_block: hex_bits = hexlify(bt_obj.bits) self.current_net['difficulty'] = bits_to_difficulty(hex_bits) self.current_net['subsidy'] = bt_obj.total_value self.current_net['height'] = bt_obj.block_height - 1 self.current_net['prev_hash'] = bt_obj.hashprev_be_hex self.current_net['transactions'] = len(bt_obj.transactions)
def test_block_header3(self): # pulled from heavycoin blockchain and modded slightly with full block header block_data = { 'bits': '1c040773', 'hash': '000000000035ef615d0708b7409021bde4e29ab063f7f8b531197d8230a931ca', 'height': 122588, 'merkleroot': '105faebb2ac14858bbf92d1ebd29dd49b5fd532a54097bc76818e3668dd6925b', 'nonce': hexlify(pack(str(">L"), 825226627)), 'previousblockhash': '0000000003d8d235668c6d41389dd703b828f4a0f02d30a37e62ca10ab144b3f', 'curtime': 1409012334, "vote": 1, "reward": 434, 'tx': [('105faebb2ac14858bbf92d1ebd29dd49b5fd532a54097bc76818e3668dd6925b', '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1103dcde010453fbd26e7b00000b7b000000ffffffff010072d71a0a0000001976a914caf784fd449b3cdde48c7542640f14b34aed42fd88ac00000000' )], 'version': 350470948 } # make a list of objects # confirm that the objects hash correctly coinbase = None transactions = [ Transaction(unhexlify(data.encode('ascii')), disassemble=True, coin='HVC') for _, data in block_data['tx'] ] self.assertEquals( hexlify(merkleroot(transactions, be=True, hash_func=hvc_hash)[0]).decode('ascii'), block_data['merkleroot']) for obj, hsh in zip(transactions, block_data['tx']): hsh = hsh[0] obj.disassemble() self.assertEquals(obj.behexhash, hsh) if obj.is_coinbase: idx = transactions.index(obj) coinbase = transactions.pop(idx) print("Found coinbase idx {} Amount is {}".format( idx, coinbase.outputs[0].amount)) pprint(coinbase.to_dict()) tmplt = BlockTemplate.from_gbt(block_data, coinbase, transactions=transactions, coin='HVC') self.assertEquals( hexlify(tmplt.merkleroot_be(coinbase)).decode('ascii'), block_data['merkleroot']) header = tmplt.block_header(block_data['nonce'], b'', b'', nvote=hexlify( pack(str(">H"), block_data['vote']))) self.assertEquals(block_data['hash'], hexlify(hvc_powhash(header)[::-1]).decode('ascii'))
def test_block_header4(self): # pulled from mozzshare blockchain and modded slightly with full block header block_data = { 'bits': '1b65bf06', 'hash': '00000000005f53a4158ec81a7f936875b117add6ddf16052be69db814ed08e5d', 'height': 95477, 'merkleroot': '68214acafcc87de6c6bf0389cf6fbaf85ce602972e188a422320260f28e57adc', 'nonce': hexlify(pack(str(">L"), 12348287)), 'previousblockhash': '00000000003fbdcb0529f2df6b9e846092c25fd472117f2dade884be824a519f', 'curtime': 1412981134, "reward": 120, 'tx': [ ('273de181f48d64299a81b16705fc0d7d9ee392e258af96a1e10a9dffce13d64c', '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2703f57401062f503253482f048661385408f80031f9dc0100000d2f7374726174756d506f6f6c2f0000000001408544cb020000001976a9145944e74d6aaf83be927703ad95e7308787bb55c588ac00000000' ), ('d4fb5de6290873a66171d167ae91cec59e116e23c2a88b50741671a76061176e', '0100000001fccce331a39a2b923ee88c7660e0803214f9a677314f21c7cb3a81e25eeb9158010000006c493046022100c13d4e43a267fa6e3b74d559eb985e886d8efe6d7f78c6105d5f6e46677e9c0d02210096e2acfc2e745286c0e948cd3f085a2da6d396277644669f448699a280bcc955012102dfaf5dfed2eec785ed262b574b2cf1c2b5e613ca5149b24725b54cb62c8e94acffffffff02f2f1b507000000001976a914ea76974eccf823d94a3615c1a0a117dec16533d888ac1ac96ae4010000001976a914e2c6ffbd9a5187e474d8278f4d2ad4f2adad17cf88ac00000000' ), ('2daaa24179c2bf1c95da24f8ea3bf4d2722326c03f6407676b23edb42d0c908d', '010000000154f1cf0ee5b7efd7e3f3d3e1b81d98c743c0b1ab8257a82119ac6d0e73362876000000006b4830450220153fb11ac82d3d97addb82e717d5cc95c0d4c31dc709d4f634f82d5c93bb2730022100b12f8251a57238bbaa5c1e6c6552c1d64671a0130a24afcffad723b6fe0f8bfc0121024a192ab361b42cb0aab26e877423b4c51402d98423912f5faad243a301285e9dffffffff02ca154407000000001976a914b652ec06255ecd4708f416e8ae17646b773d09e088acef47e09c010000001976a9149212ca9f86bc8f699f31be024d092963661a0eff88ac00000000' ), ], 'version': 325781176 } # make a list of objects # confirm that the objects hash correctly coinbase = None transactions = [ Transaction(unhexlify(data.encode('ascii')), disassemble=True, coin='MLS') for _, data in block_data['tx'] ] self.assertEquals( hexlify(merkleroot(transactions, be=True, hash_func=hvc_hash)[0]).decode('ascii'), block_data['merkleroot']) for obj, hsh in zip(transactions, block_data['tx']): hsh = hsh[0] obj.disassemble() self.assertEquals(obj.behexhash, hsh) if obj.is_coinbase: idx = transactions.index(obj) coinbase = transactions.pop(idx) print("Found coinbase idx {} Amount is {}".format( idx, coinbase.outputs[0].amount)) pprint(coinbase.to_dict()) tmplt = BlockTemplate.from_gbt(block_data, coinbase, transactions=transactions, coin='MLS') self.assertEquals( hexlify(tmplt.merkleroot_be(coinbase)).decode('ascii'), block_data['merkleroot']) header = tmplt.block_header(block_data['nonce'], b'', b'') self.assertEquals(block_data['hash'], hexlify(hvc_powhash(header)[::-1]).decode('ascii'))
def test_block_header(self): # pulled from dogecoin blockchain and modded slightly, height 50000 block_data = { 'bits': '1c00c7ec', 'hash': 'e4cab588a33147a217c8bc2f923fcd1f642fde26c7c797a5c2c4808c4c617a7e', 'height': 50000, 'merkleroot': '5af19843e6220965f3c4b5f8d3995796edff0aad5ee4cc6ab849333881c001e1', 'nonce': hexlify(pack(str(">L"), 3014526208)), 'previousblockhash': '7f0502292609f5949260403176e8874ec7c5376397641d3f660648e7fdda0bab', 'curtime': 1389349309, 'tx': [('d4eeb0cd44a4339d1a9fe8cea9482689165cff6ed1a3f4815bafcb443cdd271e', '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff250350c300062f503253482f04bcc9cf5208f80032c0020000000b2f4e757432506f6f6c732f00000000010b54074f2f4b00001976a9141b3fdba0ff497a9d8e84d65e9b078e23e46e97f088ac00000000' ), ('481fce0e2572dd782369dcc2457c267bb70918e825dfedd6e178ddf28d016cde', '0100000006ca0667d0021af9199f134a38ece3f813200f457282c2904d11b5c14a773ad514010000006b48304502200470b7b254b1d4b75f3318c6348ac97e2ba35f4be838aa10b50429107b4a8026022100864687886781c4a16de03b18fc5236bd379a6655f075b75da308b9bc3d7565b801210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff9f9316e06ba91ceb8d93a3f3b1286570ccc9f5720c944a556e37a11a0789d99a010000006b483045022100fc72dadfb07f7253b948bd94c3e9e534a4f45fdd70d6f25e865afc079f5f07ac022028be9ec4d68e700f5843a72e387f1a2bdf0cacb06f19ad8fbbc3db15ceaadd9401210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffffcc803949dfe10ac0d9b7adc4b560a52fdbfc3fbf5a2cd4c515bdc1ccee1919e6010000006a4730440220385fb14c3f6559557c6ff0a1beab9c0a3a1f3ee7b5c466874566f25063be13fb022001b84680899638bd99fcea1f8a007ccda4df954a594210a1e6b2644c681b56b601210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff340b8f285ceea5a123aaae8d33fd940442962894b8276a0f1d0959072c85b1e2010000006b4830450221008c592892fb530499f75065d4c741a97bb89e7aab7f156cf6471e134e41d19b3802202355e2f57c8ab27eb9445612e18f8ca0c51d0c8bad035082f784157db006419501210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff57835281cd613c9afc81e339d4d6b5ff20e25b381015b99ad863c3cec9c456d3010000006b48304502201ebdf69c6e146dc9f832d32439a1fef26011eecac1ffdc246ed37b726ca33f9a022100bb4e8f82c2c89c254700196f08e277af6964a04122a6395d42e61ffdbbb08ac601210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff5d22d9f2819f918f92bfb529c58201ed382a38d67a20c4277d104ecd92cb84c1010000006c493046022100834cfab229487cef0fe22dd07bbcf83bd3ce2696aee904b906c0cd8791e86a74022100a9dc16328ca8c648a0999d584fb1003fd4375aabe05d273143e976a735cf72f001210314309009775cf62a41fb7f0527e5bcda061e0b1f9dde2dc9cec7c7675453e901ffffffff010011133a4f0200001976a914db4d7ba9e58109182b04307cd587fc0c79f659e788ac00000000' ), ('74c60a18fc9a265cf78973d36e20b781ae427d4d62e9818bc51de000ef6fb1e3', '0100000006ec24cf82341457fe1f4e5a8aa70cd0cd24577d55de00f0220687edb5c4b18f48000000006b483045022100ca65846ad4fddc08b6e36853f6d0175df6a967c0c76c2804f07e3efe4a07901002207b67d8a7ee76dcf45f9515589381cccd2db33cb602b85f3f1abc873bfd2bb0680121037c533b95e310e28de6ea11179dac58b5a44e6de5eb37aacdfcb33c7d3c241cdaffffffffdf50b75ca17711f4e7060684389eaba1f9b928e160021ceb5348d338ecc827fa010000006a47304402202eca6850b9c25e8974441dd386b58d9a615ab8527f46890d10861916ee25b87f02201d35044b1ec20d6d2ec2e292354acc5d65ae8be10bf57cbd17d51ce0ac07dd6c012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150fffffffff6157a915b05f2c5d823123fb4b230b045fdbff0f68578d44de49f78cf0ede5b010000006b48304502201b61e8486f039524fb5a2fd51fdc75c170502a4adc43fdfefb830abcf572b4d0022100b5f134301962e661e8251f79182344d879dbd990c0513b6ee96ad45ffb5b9475012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150ffffffffd48aed18cad37e0d0f5bbcdd1b7f0a56899bdae664164dab0e243be02761a9ba010000006b483045022100c0c06d4fb2ec9f473f3b5fe9e96ab1379f9431cc639256a780d395b8c816a801022049c38e24224e02c7d103dbb4ffe650f86a85efbbd06e09f14bb68289813a88ce012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150ffffffffbb1f79fb8ef08188bbe5720afc51e8d4b17b227ac0575e06b113e9aa2cf73c9a010000006b483045022100de8dfc418ca7e9cbc3b8cee601e06ded8519490c3e3a69ad445e4bdbae6646a50220675e874008150d6c407211588819d42640e873cd5124a6acedfc66c7f5aae088012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150ffffffffdc2dff24673739435ef472cedc633557f21c6909822a40136c4b75c08797f6f7010000006a473044022045c537da10375757cb53ec70c06c6f00011c0759bdf8e6df2a012ce33153c7e102206e1e35d6f57c7de5ad37272efdecaf7313ada0e8f66f9532dd0747b09cb8003e012102720d51af78046f9454039c958c592358e637e46b43184f837d6a5572056a5150ffffffff0231cc5893000000001976a914262fee33cefdf111c3515c0502ba69073609904888ac0038c566e40200001976a914bd007a5819157bc912364e54fe7e0551df06d54888ac00000000' ), ('12127842a829a588be0ac25a3ab5505925e79440d6911d22dd1059c79b9966e7', '0100000002ac367cb4c2ebd51066b0a87bac57a0f4876a6be781b1d2935bb49d1320c735f7000000006b48304502207a58e61dbe5ab39e7f6b3a91bf62a33102dfc6e7d2270d0f2cd31d0fd2b58a4a022100f27ffec655cf27ce9d4facbc9ce1b079f0c7b3fe80db69111695825e23628209012102c9619b298f6272cd0710ff5997f38dfd39096e3c95b92f857da6f4843e4afbc7ffffffff6c7fc27f6debb16714276edf12868ab5deab5a0cd64c5152928a2f3798e1ee01000000006c493046022100895790720f307d12a41a6df11c83fe2c991a857e63048e74dde91fb672a368ea022100bf9dac5625b454ee38dba9fc7e8e911bf5dc8cf504709413a9b12adb9b77bc26012103dedeabe4b6166d97479db844ca2defbbb8c5394b7576aafd6be67175062dc4beffffffff01008fd3ac8b0000001976a91464e5cfa48bf5f22ce5a5e739667ef61afb0b192d88ac00000000' ), ('48d5713b929e28f1810cc7effdbfb0cb11bc88068bfbb1c9f076536b794715af', '010000000501049ee2eba48bf43470d5a6c1328fc29e3f434cae2f92d6b55bc6b694083d4f010000006a4730440220656c1c8c96ac2e72451e1ac69e496ee3768cd35741a4605579a770bde17ebe8e02207b0093e79f4f9e99795575699fd38496b2b12ce1b994054505c52dc9092a2bba01210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffff17765b1d45fc0f0b0d9e060291bfaf19b9a219f3a74fb1fb6e20fda3278f252f010000006b4830450220264601cb87ca3579f4653248dfb539bd13bea41c63d378ab7ec86f5476ea99ab022100e5921d7d5b7c5421cf17a46729d1e08b26cfe3d64120d46a4c0faa623f91791301210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffff94a09224a810a12eb793cb87802f58e31e5fc1f38831be221a4d2f9910893dcb010000006c4930460221008160791119449e306c9db0b3f22dde248bbc73babd732052b26a58b29c86287b022100ccdd9cb9eb9d8791c45b691d09f5aba2bfd69f279edd9c8177942748a5075ed901210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffff9c9431729394c53656a625755dd2ff124a0d615932a2ea6befafd36812301ae0010000006a47304402202930eda28e865b8d190a305611473760df2b151175bc834155bc58435e82955102206eb77aceae4149715002ea4fee6eb2ffd807c1dc3fd37ea1d7eb28e88733883401210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffffae32166a760609d5fae1361f568977fb23e108a480a328298f075603a2bd124d010000006b48304502210094586a38aaa22ac5f1cb85871236057ae2fb8412fe5128768f2435eeadff0d8402202bd0000b2ddad2f1da83cfec60513aea0724f477bb55d5150e0de31955710c0d01210345c22d21ffaed0e492bd96c65f512757b7062964c8a69cbcbdc08d4139cdf561ffffffff0100bccfcd5a0000001976a91442f319a15fd608b67ddddcc2e3badf1b90554fe088ac00000000' ), ('212499f6527467b49cdf946524b42266ca8751616a41b7181d3a8931da7b240e', '01000000018fa554391f4e45ae96b9c6e9bd35f1cf38737ee9ad67ba401329df7497625513000000006b483045022019331f164d059057e5dc12bab158678ba65faf1b9542811434ce13e1179550b4022100e2375b7f350b18c1bde49c8ddd7d245282b931e1a8204018bb01b8abff94f4790121038f63d5c111706674f50f3d28729fe9848e9a3aee0b965c674d14e2155c5ee35bffffffff0200a3af46bb0000001976a9149cc45c2d78927f22ab4008a907becf6ab642258288aca019f807000000001976a9144d4952746b2777f3d07a89ad0ffd02e2e1d9aed288ac00000000' ), ('47bce257235ab0f6c8526a12fc23e3bec10bbd1d87a3a261f624ff5c0d2730f2', '0100000001a16eaa30e2d56f3a3a94bf4b640c2ffaa3ccd09ce3926567b2e63e3f4d55df26000000006b48304502203579c6afe9b7365252c0424aa5e3b9d00c850f2beee0c0b2f66ac7652c4eb002022100ca44a4a0e7d2ff81de117efd144a89c70acc87eaf2e3454fed175f0ef9b6f562012103f5f17ee5b476803cf332d56c293ce59aa77a5046a284c9f7a523fefd911e9980ffffffff0260119d0a010000001976a9149e027ca13765f6558fcbab6acec2fc561fc5dd3288ac9ec1764b010000001976a914957acdecb251260da54dc8510497cb761db0698c88ac00000000' ), ('6b74e1534ee50fc7158593831e322717fa536d65cbeb03fe53b30b31853fa033', '010000000153323c2dbba06bc58227354b013d31ba01ba814fa5681fb211c039979cac1643000000006b483045022054f7930a06af7da2a3e35c3fbb9bb0fbf5897c9296286de46d96763ea8c1d8fc0221009b8d53d5964a57fc8469ece1b0ef6f43e2cdbe1d4d72308d04491cb4705f95ce012103bbd11e6843ebcbcd2024265332738d8e1ebc7d0bb4b3a2e5b7a8bd6ece14bcb7ffffffff029feec0e4000000001976a91481d7f7a63b4a4e236b3b5d43067212e52bcd25ae88acf73d7520000000001976a91447cd32e2669d30d86d1c6708b890b9d7c7b632f188ac00000000' )], 'version': 1 } # make a list of objects # confirm that the objects hash correctly coinbase = None transactions = [ Transaction(unhexlify(data.encode('ascii')), disassemble=True) for _, data in block_data['tx'] ] self.assertEquals( hexlify(merkleroot(transactions, be=True)[0]).decode('ascii'), block_data['merkleroot']) for obj, hsh in zip(transactions, block_data['tx']): hsh = hsh[0] obj.disassemble() self.assertEquals(obj.behexhash, hsh) if obj.is_coinbase: idx = transactions.index(obj) coinbase = transactions.pop(idx) print("Found coinbase idx {} Amount is {}".format( idx, coinbase.outputs[0].amount)) tmplt = BlockTemplate.from_gbt(block_data, coinbase, transactions=transactions) self.assertEquals( hexlify(tmplt.merkleroot_be(coinbase)).decode('ascii'), block_data['merkleroot']) header = tmplt.block_header(block_data['nonce'], b'', b'') self.assertEquals(block_data['hash'], hexlify(sha256d(header)[::-1]).decode('ascii'))
def generate_job(self, push=False, flush=False, new_block=False, network='main'): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if not self._last_gbt: self.logger.warn("Cannot generate new job, missing last GBT info") return if self.auxmons: merged_work = {} auxdata = {} for auxmon in self.auxmons: if auxmon.last_work['hash'] is None: continue merged_work[auxmon.last_work['chainid']] = dict( hash=auxmon.last_work['hash'], target=auxmon.last_work['type'] ) tree, size = bitcoin_data.make_auxpow_tree(merged_work) mm_hashes = [merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size)] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack(dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) for auxmon in self.auxmons: if auxmon.last_work['hash'] is None: continue data = dict(target=auxmon.last_work['target'], hash=auxmon.last_work['hash'], height=auxmon.last_work['height'], found_block=auxmon.found_block, index=mm_hashes.index(auxmon.last_work['hash']), type=auxmon.last_work['type'], hashes=mm_hashes) auxdata[auxmon.config['currency']] = data else: auxdata = {} mm_data = None # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.manager.config['extranonce_size'] + self.manager.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self._last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) coinbase_value = self._last_gbt['coinbasevalue'] # Payout Darkcoin masternodes mn_enforcement = self._last_gbt.get('enforce_masternode_payments', True) if (self.config['payout_drk_mn'] is True or mn_enforcement is True) \ and self._last_gbt.get('payee', '') != '': # Grab the darkcoin payout amount, default to 20% payout = self._last_gbt.get('payee_amount', coinbase_value / 5) coinbase_value -= payout coinbase.outputs.append( Output.to_address(payout, self._last_gbt['payee'])) self.logger.debug( "Created TX output for masternode at ({}:{}). Coinbase value " "reduced to {}".format(self._last_gbt['payee'], payout, coinbase_value)) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(coinbase_value, self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self._job_counter)) bt_obj = BlockTemplate.from_gbt(self._last_gbt, coinbase, extranonce_length, [Transaction(unhexlify(t['data']), fees=t['fee']) for t in self._last_gbt['transactions']]) # add in our merged mining data if mm_data: hashes = [bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.merged_data = auxdata bt_obj.job_id = job_id bt_obj.diff1 = self.config['diff1'] bt_obj.algo = self.config['algo'] bt_obj.currency = self.config['currency'] bt_obj.pow_block_hash = self.config['pow_block_hash'] bt_obj.block_height = self._last_gbt['height'] bt_obj.acc_shares = set() if flush: bt_obj.type = 0 elif push: bt_obj.type = 1 else: bt_obj.type = 2 bt_obj.found_block = self.found_block # Push the fresh job to users after updating details self._job_counter += 1 if flush: self.jobs.clear() self.jobs[job_id] = bt_obj self.latest_job = bt_obj self.new_job.job = bt_obj self.new_job.set() self.new_job.clear() event = ("{name}.jobmanager.new_job:1|c\n" .format(name=self.manager.config['procname'])) if push or flush: self.logger.info( "{}: New block template with {:,} trans. " "Diff {:,.4f}. Subsidy {:,.2f}. Height {:,}. Merged: {}" .format("FLUSH" if flush else "PUSH", len(self._last_gbt['transactions']), bits_to_difficulty(self._last_gbt['bits']), self._last_gbt['coinbasevalue'] / 100000000.0, self._last_gbt['height'], ', '.join(auxdata.keys()))) event += ("{name}.jobmanager.work_push:1|c\n" .format(name=self.manager.config['procname'])) # Stats and notifications now that it's pushed if flush: event += ("{name}.jobmanager.work_restart:1|c\n" .format(name=self.manager.config['procname'])) self.logger.info("New {} network block announced! Wiping previous" " jobs and pushing".format(network)) elif push: self.logger.info("New {} network block announced, pushing new job!" .format(network)) if new_block: hex_bits = hexlify(bt_obj.bits) self.current_net['difficulty'] = bits_to_difficulty(hex_bits) self.current_net['subsidy'] = bt_obj.total_value self.current_net['height'] = bt_obj.block_height - 1 self.current_net['last_block'] = time.time() self.current_net['prev_hash'] = bt_obj.hashprev_be_hex self.current_net['transactions'] = len(bt_obj.transactions) event += ( "{name}.{curr}.difficulty:{diff}|g\n" "{name}.{curr}.subsidy:{subsidy}|g\n" "{name}.{curr}.job_generate:{t}|g\n" "{name}.{curr}.height:{height}|g" .format(name=self.manager.config['procname'], curr=self.config['currency'], diff=self.current_net['difficulty'], subsidy=bt_obj.total_value, height=bt_obj.block_height - 1, t=(time.time() - self._last_gbt['update_time']) * 1000)) self.manager.log_event(event)
def test_stratum_confirm(self): """ Test some raw data from cgminer submitting a share, confirm hashes come out the same as cgminer. Raw stratum params: """ gbt = { u'bits': u'1e00e92b', u'coinbaseaux': { u'flags': u'062f503253482f' }, u'coinbasevalue': 5000000000, u'curtime': 1392509565, u'height': 203588, u'mintime': 1392508633, u'mutable': [u'time', u'transactions', u'prevblock'], u'noncerange': u'00000000ffffffff', u'previousblockhash': u'b0f5ecb62774f2f07fdc0f72fa0585ae3e8ca78ad8692209a355d12bc690fb73', u'sigoplimit': 20000, u'sizelimit': 1000000, u'target': u'000000e92b000000000000000000000000000000000000000000000000000000', u'transactions': [], u'version': 2 } extra1 = '0000000000000000' submit = { 'extra2': '00000000', 'nonce': 'd5160000', 'result': '000050ccfe8a3efe93b2ee33d2aecf4a60c809995c7dd19368a7d00c86880f30' } # build a block template object from the raw data coinbase = Transaction() coinbase.version = 2 coinbase.inputs.append(Input.coinbase(gbt['height'], [b'\0' * 12])) coinbase.outputs.append( Output.to_address(gbt['coinbasevalue'], 'D7QJyeBNuwEqxsyVCLJi3pHs64uPdMDuBa')) transactions = [] for trans in gbt['transactions']: new_trans = Transaction(unhexlify(trans['data']), fees=trans['fee']) assert trans['hash'] == new_trans.lehexhash transactions.append(new_trans) bt = BlockTemplate.from_gbt(gbt, coinbase, 12, transactions) send_params = bt.stratum_params() print( "job_id: {0}\nprevhash: {1}\ncoinbase1: {2}\ncoinbase2: {3}" "\nmerkle_branch: {4}\nversion: {5}\nnbits: {6}\nntime: {7}". format(*send_params)) header = bt.block_header(submit['nonce'], extra1, submit['extra2']) target = target_from_diff( 1, 0x0000FFFF00000000000000000000000000000000000000000000000000000000) self.assertEquals( hexlify(sha256d(header)[::-1]).decode('ascii'), submit['result']) assert hash_int < target
def generate_job(self, push=False, flush=False, new_block=False): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if not self._last_gbt: self.logger.warn("Cannot generate new job, missing last GBT info") return if self.merged_work: tree, size = bitcoin_data.make_auxpow_tree(self.merged_work) mm_hashes = [self.merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size)] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack(dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) merged_data = {} for aux_work in self.merged_work.itervalues(): data = dict(target=aux_work['target'], hash=aux_work['hash'], height=aux_work['height'], index=mm_hashes.index(aux_work['hash']), type=aux_work['type'], hashes=mm_hashes) merged_data[aux_work['type']] = data else: merged_data = {} mm_data = None self.logger.info("Generating new block template with {} trans. " "Diff {:,.4f}. Subsidy {:,.2f}. Height {:,}. " "Merged chains: {}" .format(len(self._last_gbt['transactions']), bits_to_difficulty(self._last_gbt['bits']), self._last_gbt['coinbasevalue'] / 100000000.0, self._last_gbt['height'], ', '.join(merged_data.keys()))) # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.config['extranonce_size'] + self.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self._last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) # Darkcoin payee amount if self._last_gbt.get('payee', '') != '': payout = self._last_gbt['coinbasevalue'] / 5 self._last_gbt['coinbasevalue'] -= payout coinbase.outputs.append( Output.to_address(payout, self._last_gbt['payee'])) self.logger.info("Paying out masternode at addr {}. Payout {}. Blockval reduced to {}" .format(self._last_gbt['payee'], payout, self._last_gbt['coinbasevalue'])) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(self._last_gbt['coinbasevalue'], self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self._job_counter)) bt_obj = BlockTemplate.from_gbt(self._last_gbt, coinbase, extranonce_length, [Transaction(unhexlify(t['data']), fees=t['fee']) for t in self._last_gbt['transactions']]) # add in our merged mining data if mm_data: hashes = [bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.merged_data = merged_data bt_obj.job_id = job_id bt_obj.diff1 = self.config['diff1'] bt_obj.algo = self.config['algo'] bt_obj.pow_block_hash = self.config['pow_block_hash'] bt_obj.block_height = self._last_gbt['height'] bt_obj.acc_shares = set() if push: if flush: self.logger.info("New work announced! Wiping previous jobs...") self.jobs.clear() self.latest_job = None else: self.logger.info("New work announced!") self._job_counter += 1 self.jobs[job_id] = bt_obj self.latest_job = job_id if push: t = time.time() bt_obj.stratum_string() for idx, client in viewitems(self.stratum_manager.clients): try: if client.authenticated: client._push(bt_obj, flush=flush) except AttributeError: pass self.logger.info("New job enqueued for transmission to {} users in {}" .format(len(self.stratum_manager.clients), time_format(time.time() - t))) if flush: self.server['work_restarts'].incr() self.server['work_pushes'].incr() self.server['new_jobs'].incr() if new_block: hex_bits = hexlify(bt_obj.bits) self.current_net['difficulty'] = bits_to_difficulty(hex_bits) self.current_net['subsidy'] = bt_obj.total_value self.current_net['height'] = bt_obj.block_height - 1 self.current_net['prev_hash'] = bt_obj.hashprev_be_hex self.current_net['transactions'] = len(bt_obj.transactions)
def generate_job(self, push=False, flush=False, new_block=False): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if self.last_gbt is None: return merged_work = self.net_state['merged_work'] if self.net_state['merged_work']: tree, size = bitcoin_data.make_auxpow_tree(merged_work) mm_hashes = [merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size)] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack(dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) mm_later = [(aux_work, mm_hashes.index(aux_work['hash']), mm_hashes) for chain_id, aux_work in merged_work.iteritems()] else: mm_later = [] mm_data = None with self.job_lock: # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.config['extranonce_size'] + self.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self.last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(self.last_gbt['coinbasevalue'] - self.last_gbt['fees'], self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self.net_state['job_counter'])) logger.info("Generating new block template with {} trans. Diff {}. Subsidy {}. Fees {}." .format(len(self.net_state['transactions']), bits_to_difficulty(self.last_gbt['bits']), self.last_gbt['coinbasevalue'], self.last_gbt['fees'])) bt_obj = BlockTemplate.from_gbt(self.last_gbt, coinbase, extranonce_length, []) bt_obj.mm_later = copy(mm_later) # hashes = [bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None], 0) bt_obj.job_id = job_id bt_obj.block_height = self.last_gbt['height'] bt_obj.acc_shares = set() if push: if flush: logger.info("New work announced! Wiping previous jobs...") self.net_state['jobs'].clear() self.net_state['latest_job'] = None else: logger.info("New work announced!") self.net_state['job_counter'] += 1 self.net_state['jobs'][job_id] = bt_obj self.net_state['latest_job'] = job_id if push: for idx, client in viewitems(self.stratum_clients): try: if flush: client.new_block_event.set() else: client.new_work_event.set() except AttributeError: pass if new_block: if self.config['send_new_block']: hex_bits = hexlify(bt_obj.bits) self.celery.send_task_pp('new_block', bt_obj.block_height, hex_bits, bt_obj.total_value) self.net_state['difficulty'] = bits_to_difficulty(hex_bits)