From: alex Date: Thu, 7 Mar 2013 15:39:44 +0000 (+0400) Subject: NVC/PPC protocol changes support X-Git-Url: https://git.novaco.in/?p=p2pool.git;a=commitdiff_plain;h=b68358960962d67623aa95e63c107695a856fab1 NVC/PPC protocol changes support --- diff --git a/README b/README index 1262a83..ab9b2a7 100644 --- a/README +++ b/README @@ -1,6 +1,7 @@ Requirements: Generic: - Bitcoin >=0.6.4 + Novacoin >= 0.3.1 + ltc_scrypt Python Twisted python-argparse (for Python <=2.6) @@ -15,27 +16,9 @@ Requirements: Install Zope.Interface: http://pypi.python.org/pypi/zope.interface/3.8.0 Unzip the files into C:\Python27\Lib\site-packages -Running P2Pool: - To use P2Pool, you must be running your own local bitcoind. For standard - configurations, using P2Pool should be as simple as: - - python run_p2pool.py - - Then run your miner program, connecting to 127.0.0.1 on port 9332 with any - username and password. - - If you are behind a NAT, you should enable TCP port forwarding on your - router. Forward port 9333 to the host running P2Pool. - - Run "python run_p2pool.py --help" for additional options. - -Donations towards further development: - 1HNeqi3pJRNvXybNX4FKzZgYJsdTSqJTbk - -Notes for Litecoin: - Requirements: - In order to run P2Pool with the Litecoin network, you would need to build and install the - ltc_scrypt module that includes the scrypt proof of work code that Litecoin uses for hashes. +Additional info about scrypt module: + In order to run P2Pool with the NovaCoin network, you would need to build and install the + ltc_scrypt module that includes the scrypt proof of work code that NovaCoin uses for block hashes. Linux: cd litecoin_scrypt @@ -50,14 +33,20 @@ Notes for Litecoin: If you run into an error with unrecognized command line option '-mno-cygwin', see this: http://stackoverflow.com/questions/6034390/compiling-with-cython-and-mingw-produces-gcc-error-unrecognized-command-line-o - - Running P2Pool: - Run P2Pool with the "--net litecoin" option. - Run your miner program, connecting to 127.0.0.1 on port 9327. - Forward port 9338 to the host running P2Pool. - - Litecoin's use of ports 9332 and 9332 conflicts with P2Pool running on - the Bitcoin network. To avoid problems, add these lines to litecoin.conf - and restart litecoind: - rpcport=10332 - port=10333 + +Running P2Pool: + To use P2Pool, you must be running your own local bitcoind. For standard + configurations, using P2Pool should be as simple as: + + python run_p2pool.py --net novacoin + + Then run your miner program, connecting to 127.0.0.1 on port 8336 with any + username and password. + + If you are behind a NAT, you should enable TCP port forwarding on your + router. Forward port 9777 to the host running P2Pool. + + Run "python run_p2pool.py --help" for additional options. + +Donations towards further development: + 1HNeqi3pJRNvXybNX4FKzZgYJsdTSqJTbk diff --git a/p2pool/bitcoin/data.py b/p2pool/bitcoin/data.py index de6d405..0ce113b 100644 --- a/p2pool/bitcoin/data.py +++ b/p2pool/bitcoin/data.py @@ -92,6 +92,7 @@ address_type = pack.ComposedType([ tx_type = pack.ComposedType([ ('version', pack.IntType(32)), + ('timestamp', pack.IntType(32)), ('tx_ins', pack.ListType(pack.ComposedType([ ('previous_output', pack.PossiblyNoneType(dict(hash=0, index=2**32 - 1), pack.ComposedType([ ('hash', pack.IntType(256)), @@ -130,6 +131,7 @@ block_header_type = pack.ComposedType([ block_type = pack.ComposedType([ ('header', block_header_type), ('txs', pack.ListType(tx_type)), + ('signature', pack.VarStrType()), ]) # merged mining diff --git a/p2pool/bitcoin/helper.py b/p2pool/bitcoin/helper.py index a46f5fd..d4fb288 100644 --- a/p2pool/bitcoin/helper.py +++ b/p2pool/bitcoin/helper.py @@ -62,17 +62,20 @@ def getwork(bitcoind, use_getblocktemplate=False): @deferral.retry('Error submitting primary block: (will retry)', 10, 10) def submit_block_p2p(block, factory, net): if factory.conn.value is None: - print >>sys.stderr, 'No bitcoind connection when block submittal attempted! %s%064x' % (net.PARENT.BLOCK_EXPLORER_URL_PREFIX, bitcoin_data.hash256(bitcoin_data.block_header_type.pack(block['header']))) + print >>sys.stderr, 'No bitcoind connection when block submittal attempted! %s%064x' % (net.PARENT.BLOCK_EXPLORER_URL_PREFIX, self.node.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(block['header']))) raise deferral.RetrySilentlyException() factory.conn.value.send_block(block=block) @deferral.retry('Error submitting block: (will retry)', 10, 10) @defer.inlineCallbacks def submit_block_rpc(block, ignore_failure, bitcoind, bitcoind_work, net): + if bitcoind_work.value['use_getblocktemplate']: result = yield bitcoind.rpc_submitblock(bitcoin_data.block_type.pack(block).encode('hex')) success = result is None else: + print bitcoin_data.block_type.pack(block).encode('hex') + result = yield bitcoind.rpc_getmemorypool(bitcoin_data.block_type.pack(block).encode('hex')) success = result success_expected = net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(block['header'])) <= block['header']['bits'].target @@ -80,5 +83,5 @@ def submit_block_rpc(block, ignore_failure, bitcoind, bitcoind_work, net): print >>sys.stderr, 'Block submittal result: %s (%r) Expected: %s' % (success, result, success_expected) def submit_block(block, ignore_failure, factory, bitcoind, bitcoind_work, net): - submit_block_p2p(block, factory, net) + #submit_block_p2p(block, factory, net) submit_block_rpc(block, ignore_failure, bitcoind, bitcoind_work, net) diff --git a/p2pool/bitcoin/networks.py b/p2pool/bitcoin/networks.py index ea53f05..8831470 100644 --- a/p2pool/bitcoin/networks.py +++ b/p2pool/bitcoin/networks.py @@ -5,156 +5,66 @@ from twisted.internet import defer from . import data from p2pool.util import math, pack +from operator import * + +def get_subsidy(nCap, nMaxSubsidy, bnTarget): + bnLowerBound = 0.01 + bnUpperBound = bnSubsidyLimit = nMaxSubsidy + bnTargetLimit = 0x00000fffff000000000000000000000000000000000000000000000000000000 + + while bnLowerBound + 0.01 <= bnUpperBound: + bnMidValue = (bnLowerBound + bnUpperBound) / 2 + if pow(bnMidValue, nCap) * bnTargetLimit > pow(bnSubsidyLimit, nCap) * bnTarget: + bnUpperBound = bnMidValue + else: + bnLowerBound = bnMidValue + + nSubsidy = round(bnMidValue, 2) + + if nSubsidy > bnMidValue: + nSubsidy = nSubsidy - 0.01 + + return int(nSubsidy * 1000000) nets = dict( - bitcoin=math.Object( - P2P_PREFIX='f9beb4d9'.decode('hex'), - P2P_PORT=8333, - ADDRESS_VERSION=0, - RPC_PORT=8332, + novacoin=math.Object( + P2P_PREFIX='e4e8e9e5'.decode('hex'), + P2P_PORT=7777, + ADDRESS_VERSION=8, + RPC_PORT=8344, RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue( - 'bitcoinaddress' in (yield bitcoind.rpc_help()) and + 'novacoinaddress' in (yield bitcoind.rpc_help()) and not (yield bitcoind.rpc_getinfo())['testnet'] )), - SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//210000, - POW_FUNC=data.hash256, - BLOCK_PERIOD=600, # s - SYMBOL='BTC', - CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf'), - BLOCK_EXPLORER_URL_PREFIX='http://blockexplorer.com/block/', - ADDRESS_EXPLORER_URL_PREFIX='http://blockexplorer.com/address/', - SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1), - ), - bitcoin_testnet=math.Object( - P2P_PREFIX='0b110907'.decode('hex'), - P2P_PORT=18333, - ADDRESS_VERSION=111, - RPC_PORT=18332, - RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue( - 'bitcoinaddress' in (yield bitcoind.rpc_help()) and - (yield bitcoind.rpc_getinfo())['testnet'] - )), - SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//210000, - POW_FUNC=data.hash256, - BLOCK_PERIOD=600, # s - SYMBOL='tBTC', - CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Bitcoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Bitcoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.bitcoin'), 'bitcoin.conf'), - BLOCK_EXPLORER_URL_PREFIX='http://blockexplorer.com/testnet/block/', - ADDRESS_EXPLORER_URL_PREFIX='http://blockexplorer.com/testnet/address/', - SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1), - ), - - namecoin=math.Object( - P2P_PREFIX='f9beb4fe'.decode('hex'), - P2P_PORT=8334, - ADDRESS_VERSION=52, - RPC_PORT=8332, - RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue( - 'namecoinaddress' in (yield bitcoind.rpc_help()) and - not (yield bitcoind.rpc_getinfo())['testnet'] - )), - SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//210000, - POW_FUNC=data.hash256, - BLOCK_PERIOD=600, # s - SYMBOL='NMC', - CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Namecoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Namecoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.namecoin'), 'bitcoin.conf'), - BLOCK_EXPLORER_URL_PREFIX='http://explorer.dot-bit.org/b/', - ADDRESS_EXPLORER_URL_PREFIX='http://explorer.dot-bit.org/a/', - SANE_TARGET_RANGE=(2**256//2**32 - 1, 2**256//2**32 - 1), - ), - namecoin_testnet=math.Object( - P2P_PREFIX='fabfb5fe'.decode('hex'), - P2P_PORT=18334, - ADDRESS_VERSION=111, - RPC_PORT=8332, - RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue( - 'namecoinaddress' in (yield bitcoind.rpc_help()) and - (yield bitcoind.rpc_getinfo())['testnet'] - )), - SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//210000, - POW_FUNC=data.hash256, - BLOCK_PERIOD=600, # s - SYMBOL='tNMC', - CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Namecoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Namecoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.namecoin'), 'bitcoin.conf'), - BLOCK_EXPLORER_URL_PREFIX='http://testnet.explorer.dot-bit.org/b/', - ADDRESS_EXPLORER_URL_PREFIX='http://testnet.explorer.dot-bit.org/a/', - SANE_TARGET_RANGE=(2**256//2**32 - 1, 2**256//2**32 - 1), - ), - - litecoin=math.Object( - P2P_PREFIX='fbc0b6db'.decode('hex'), - P2P_PORT=9333, - ADDRESS_VERSION=48, - RPC_PORT=9332, - RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue( - 'litecoinaddress' in (yield bitcoind.rpc_help()) and - not (yield bitcoind.rpc_getinfo())['testnet'] - )), - SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//840000, + SUBSIDY_FUNC=lambda target: get_subsidy(6, 100, target), + BLOCKHASH_FUNC=lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)), POW_FUNC=lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)), - BLOCK_PERIOD=150, # s - SYMBOL='LTC', - CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Litecoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Litecoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.litecoin'), 'litecoin.conf'), - BLOCK_EXPLORER_URL_PREFIX='http://explorer.litecoin.net/block/', - ADDRESS_EXPLORER_URL_PREFIX='http://explorer.litecoin.net/address/', + BLOCK_PERIOD=600, # s + SYMBOL='NVC', + CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'NovaCoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/NovaCoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.novacoin'), 'novacoin.conf'), + BLOCK_EXPLORER_URL_PREFIX='http://novacoin.ru/block/', + ADDRESS_EXPLORER_URL_PREFIX='http://novacoin.ru/address/', SANE_TARGET_RANGE=(2**256//1000000000 - 1, 2**256//1000 - 1), ), - litecoin_testnet=math.Object( - P2P_PREFIX='fcc1b7dc'.decode('hex'), - P2P_PORT=19333, + novacoin_testnet=math.Object( + P2P_PREFIX='cdf2c0ef'.decode('hex'), + P2P_PORT=17777, ADDRESS_VERSION=111, - RPC_PORT=19332, + RPC_PORT=8344, RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue( - 'litecoinaddress' in (yield bitcoind.rpc_help()) and + 'novacoinaddress' in (yield bitcoind.rpc_help()) and (yield bitcoind.rpc_getinfo())['testnet'] )), - SUBSIDY_FUNC=lambda height: 50*100000000 >> (height + 1)//840000, + SUBSIDY_FUNC=lambda target: get_subsidy(6, 100, target), + BLOCKHASH_FUNC=lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)), POW_FUNC=lambda data: pack.IntType(256).unpack(__import__('ltc_scrypt').getPoWHash(data)), - BLOCK_PERIOD=150, # s - SYMBOL='tLTC', - CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Litecoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Litecoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.litecoin'), 'litecoin.conf'), - BLOCK_EXPLORER_URL_PREFIX='http://nonexistent-litecoin-testnet-explorer/block/', - ADDRESS_EXPLORER_URL_PREFIX='http://nonexistent-litecoin-testnet-explorer/address/', - SANE_TARGET_RANGE=(2**256//1000000000 - 1, 2**256 - 1), - ), - - terracoin=math.Object( - P2P_PREFIX='42babe56'.decode('hex'), - P2P_PORT=13333, - ADDRESS_VERSION=0, - RPC_PORT=13332, - RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue( - 'terracoinaddress' in (yield bitcoind.rpc_help()) and - not (yield bitcoind.rpc_getinfo())['testnet'] - )), - SUBSIDY_FUNC=lambda height: 20*100000000 >> (height + 1)//1050000, - POW_FUNC=data.hash256, - BLOCK_PERIOD=120, # s - SYMBOL='TRC', - CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Terracoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Terracoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.terracoin'), 'terracoin.conf'), - BLOCK_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/block/', - ADDRESS_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/address/', - SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1), - ), - terracoin_testnet=math.Object( - P2P_PREFIX='41babe56'.decode('hex'), - P2P_PORT=23333, - ADDRESS_VERSION=111, - RPC_PORT=23332, - RPC_CHECK=defer.inlineCallbacks(lambda bitcoind: defer.returnValue( - 'terracoinaddress' in (yield bitcoind.rpc_help()) and - (yield bitcoind.rpc_getinfo())['testnet'] - )), - SUBSIDY_FUNC=lambda height: 20*100000000 >> (height + 1)//1050000, - POW_FUNC=data.hash256, - BLOCK_PERIOD=120, # s - SYMBOL='tTRC', - CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'Terracoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/Terracoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.terracoin'), 'terracoin.conf'), - BLOCK_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/testnet/block/', - ADDRESS_EXPLORER_URL_PREFIX='http://cryptocoinexplorer.com:3750/testnet/address/', - SANE_TARGET_RANGE=(2**256//2**32//1000 - 1, 2**256//2**32 - 1), + BLOCK_PERIOD=600, # s + SYMBOL='tNVC', + CONF_FILE_FUNC=lambda: os.path.join(os.path.join(os.environ['APPDATA'], 'NovaCoin') if platform.system() == 'Windows' else os.path.expanduser('~/Library/Application Support/NovaCoin/') if platform.system() == 'Darwin' else os.path.expanduser('~/.novacoin'), 'novacoin.conf'), + BLOCK_EXPLORER_URL_PREFIX='http://nonexistent-novacoin-testnet-explorer/block/', + ADDRESS_EXPLORER_URL_PREFIX='http://nonexistent-novacoin-testnet-explorer/address/', + SANE_TARGET_RANGE=(2**256//1000000000 - 1, 2**256//1000 - 1), ), - ) for net_name, net in nets.iteritems(): net.NAME = net_name diff --git a/p2pool/bitcoin/p2p.py b/p2pool/bitcoin/p2p.py index fd403f1..6cec4b6 100644 --- a/p2pool/bitcoin/p2p.py +++ b/p2pool/bitcoin/p2p.py @@ -15,7 +15,8 @@ from p2pool.util import deferral, p2protocol, pack, variable class Protocol(p2protocol.Protocol): def __init__(self, net): p2protocol.Protocol.__init__(self, net.P2P_PREFIX, 1000000) - + self.net = net + def connectionMade(self): self.send_version( version=32200, @@ -115,7 +116,7 @@ class Protocol(p2protocol.Protocol): ('block', bitcoin_data.block_type), ]) def handle_block(self, block): - block_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(block['header'])) + block_hash = self.net.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(block['header'])) self.get_block.got_response(block_hash, block) self.get_block_header.got_response(block_hash, block['header']) @@ -125,7 +126,7 @@ class Protocol(p2protocol.Protocol): def handle_headers(self, headers): for header in headers: header = header['header'] - self.get_block_header.got_response(bitcoin_data.hash256(bitcoin_data.block_header_type.pack(header)), header) + self.get_block_header.got_response(self.net.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(header)), header) self.factory.new_headers.happened([header['header'] for header in headers]) message_ping = pack.ComposedType([]) diff --git a/p2pool/data.py b/p2pool/data.py index 5ab1e7b..0456a28 100644 --- a/p2pool/data.py +++ b/p2pool/data.py @@ -5,6 +5,7 @@ import os import random import sys import time +import math from twisted.python import log @@ -12,6 +13,8 @@ import p2pool from p2pool.bitcoin import data as bitcoin_data, script, sha256 from p2pool.util import math, forest, pack +minout = pow(10, 6) / 100; + # hashlink hash_link_type = pack.ComposedType([ @@ -56,7 +59,7 @@ class Share(object): SUCCESSOR = None other_txs = None - + small_block_header_type = pack.ComposedType([ ('version', pack.VarIntType()), ('previous_block', pack.PossiblyNoneType(0, pack.IntType(256))), @@ -70,7 +73,7 @@ class Share(object): ('previous_share_hash', pack.PossiblyNoneType(0, pack.IntType(256))), ('coinbase', pack.VarStrType()), ('nonce', pack.IntType(32)), - ('pubkey_hash', pack.IntType(160)), + ('pubkey', pack.FixedStrType(33)), ('subsidy', pack.IntType(64)), ('donation', pack.IntType(16)), ('stale_info', pack.EnumType(pack.IntType(8), dict((k, {0: None, 253: 'orphan', 254: 'doa'}.get(k, 'unk%i' % (k,))) for k in xrange(256)))), @@ -103,13 +106,30 @@ class Share(object): ('identifier', pack.FixedStrType(64//8)), ('share_info', share_info_type), ]) - - gentx_before_refhash = pack.VarStrType().pack(DONATION_SCRIPT) + pack.IntType(64).pack(0) + pack.VarStrType().pack('\x24' + pack.IntType(256).pack(0) + pack.IntType(32).pack(0))[:2] - + + + gentx_before_refhash = pack.VarStrType().pack(DONATION_SCRIPT) + pack.IntType(64).pack(minout) + pack.VarStrType().pack('\x24' + pack.IntType(256).pack(0) + pack.IntType(32).pack(0))[:2] + @classmethod def generate_transaction(cls, tracker, share_data, block_target, desired_timestamp, desired_target, ref_merkle_link, desired_other_transaction_hashes_and_fees, net, known_txs=None, last_txout_nonce=0, base_subsidy=None): previous_share = tracker.items[share_data['previous_share_hash']] if share_data['previous_share_hash'] is not None else None - + + def get_coinbase_fee(outpointsnum): + # calculate neccessary coinbase fee + coinbase_size = 59 + outpointsnum * 44 + 50 + + # if coinbase size is greater than 1000 bytes, it should pay fee (0.01 per 1000 bytes) + if coinbase_size > 1000: + return floor(coinbase_size / 1000.0) * minout + + return 0 + + if base_subsidy is None: + base_subsidy = net.PARENT.SUBSIDY_FUNC(block_target) + + # current user payout script + this_script = bitcoin_data.pubkey_to_script2(share_data['pubkey']) + height, last = tracker.get_height_and_last(share_data['previous_share_hash']) assert height >= net.REAL_CHAIN_LENGTH or last is None if height < net.TARGET_LOOKBEHIND: @@ -121,12 +141,12 @@ class Share(object): pre_target3 = math.clip(pre_target2, (net.MIN_TARGET, net.MAX_TARGET)) max_bits = bitcoin_data.FloatingInteger.from_target_upper_bound(pre_target3) bits = bitcoin_data.FloatingInteger.from_target_upper_bound(math.clip(desired_target, (pre_target3//10, pre_target3))) - + new_transaction_hashes = [] new_transaction_size = 0 transaction_hash_refs = [] other_transaction_hashes = [] - + past_shares = list(tracker.get_chain(share_data['previous_share_hash'], min(height, 100))) tx_hash_to_this = {} for i, share in enumerate(past_shares): @@ -146,32 +166,60 @@ class Share(object): this = [0, len(new_transaction_hashes)-1] transaction_hash_refs.extend(this) other_transaction_hashes.append(tx_hash) - + included_transactions = set(other_transaction_hashes) - removed_fees = [fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash not in included_transactions] - definite_fees = sum(0 if fee is None else fee for tx_hash, fee in desired_other_transaction_hashes_and_fees if tx_hash in included_transactions) - if None not in removed_fees: - share_data = dict(share_data, subsidy=share_data['subsidy'] - sum(removed_fees)) - else: - assert base_subsidy is not None - share_data = dict(share_data, subsidy=base_subsidy + definite_fees) - + + share_data = dict(share_data, subsidy=base_subsidy) + weights, total_weight, donation_weight = tracker.get_cumulative_weights(share_data['previous_share_hash'], min(height, net.REAL_CHAIN_LENGTH), 65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target), ) + + # calculate "raw" subsidy + raw_subsidy = share_data['subsidy'] - 3 * minout - get_coinbase_fee(len(weights)) + + # calculate "raw" amounts + raw_amounts = dict((script, raw_subsidy*weight//total_weight) for script, weight in weights.iteritems()) + + total_remowed_weight = 0 + + # iterate list and collect all weights, which produces less than 0.01 payout + # it's neccessary due to NVC/PPC protocol-level limitations for coinbase outpoint size + for x in raw_amounts.keys(): + if raw_amounts[x] < minout and x not in [this_script, DONATION_SCRIPT]: + total_remowed_weight = total_remowed_weight + weights[x] + del weights[x] + + total_weight = total_weight - total_remowed_weight assert total_weight == sum(weights.itervalues()) + donation_weight, (total_weight, sum(weights.itervalues()) + donation_weight) - - amounts = dict((script, share_data['subsidy']*(199*weight)//(200*total_weight)) for script, weight in weights.iteritems()) # 99.5% goes according to weights prior to this share - this_script = bitcoin_data.pubkey_hash_to_script2(share_data['pubkey_hash']) - amounts[this_script] = amounts.get(this_script, 0) + share_data['subsidy']//200 # 0.5% goes to block finder - amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + share_data['subsidy'] - sum(amounts.itervalues()) # all that's left over is the donation weight and some extra satoshis due to rounding - - if sum(amounts.itervalues()) != share_data['subsidy'] or any(x < 0 for x in amounts.itervalues()): + + + # base subsidy value calculated as: + # [subsidy - (0.01 for donation + 0.01 for current user + 0.01 for p2pool outpoint) - netfee] + my_subsidy = share_data['subsidy'] - 3 * minout - get_coinbase_fee(len(weights) + 1) + + # subsidy goes according to weights prior to this share + amounts = dict((script, my_subsidy*weight//total_weight) for script, weight in weights.iteritems()) + + # all that's left over is the donation weight and some extra satoshis due to rounding + amounts[DONATION_SCRIPT] = amounts.get(DONATION_SCRIPT, 0) + my_subsidy - sum(amounts.itervalues()) + + if sum(amounts.itervalues()) != my_subsidy or any(x < 0 for x in amounts.itervalues()): raise ValueError() - + + # add 0.01 coin to donation, to satisfy the protocol + amounts[DONATION_SCRIPT] = amounts[DONATION_SCRIPT] + minout + + # add 0.01 to current user output, to satisfy the protocol + amounts[this_script] = amounts.get(this_script, 0) + minout + +# print amounts + dests = sorted(amounts.iterkeys(), key=lambda script: (script == DONATION_SCRIPT, amounts[script], script))[-4000:] # block length limit, unlikely to ever be hit - + +# print dests + share_info = dict( share_data=share_data, far_share_hash=None if last is None and height < 99 else tracker.get_nth_parent_hash(share_data['previous_share_hash'], 99), @@ -184,21 +232,27 @@ class Share(object): new_transaction_hashes=new_transaction_hashes, transaction_hash_refs=transaction_hash_refs, ) - + gentx = dict( version=1, + # coinbase timestamp must be older than share/block timestamp + # maybe there are more elegant solution, but this hack works quite well for now + timestamp=share_info['timestamp'], tx_ins=[dict( previous_output=None, sequence=None, script=share_data['coinbase'], )], tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script] or script == DONATION_SCRIPT] + [dict( - value=0, + # add 0.01 coin to service output, to satisfy the protocol + value=minout, script='\x24' + cls.get_ref_hash(net, share_info, ref_merkle_link) + pack.IntType(32).pack(last_txout_nonce), )], lock_time=0, ) - + + #print gentx + def get_share(header, last_txout_nonce=last_txout_nonce): min_header = dict(header); del min_header['merkle_root'] share = cls(net, None, dict( @@ -246,7 +300,7 @@ class Share(object): self.target = self.share_info['bits'].target self.timestamp = self.share_info['timestamp'] self.previous_hash = self.share_data['previous_share_hash'] - self.new_script = bitcoin_data.pubkey_hash_to_script2(self.share_data['pubkey_hash']) + self.new_script = bitcoin_data.pubkey_to_script2(self.share_data['pubkey']) self.desired_version = self.share_data['desired_version'] n = set() @@ -264,7 +318,7 @@ class Share(object): merkle_root = bitcoin_data.check_merkle_link(self.gentx_hash, self.merkle_link) self.header = dict(self.min_header, merkle_root=merkle_root) self.pow_hash = net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(self.header)) - self.hash = self.header_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(self.header)) + self.hash = self.header_hash = net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(self.header)) if self.target > net.MAX_TARGET: from p2pool import p2p @@ -273,7 +327,7 @@ class Share(object): if self.pow_hash > self.target: from p2pool import p2p raise p2p.PeerMisbehavingError('share PoW invalid') - + self.new_transaction_hashes = self.share_info['new_transaction_hashes'] # XXX eww @@ -308,10 +362,24 @@ class Share(object): raise p2p.PeerMisbehavingError('''%s can't follow %s''' % (type(self).__name__, type(previous_share).__name__)) other_tx_hashes = [tracker.items[tracker.get_nth_parent_hash(self.hash, share_count)].share_info['new_transaction_hashes'][tx_count] for share_count, tx_count in self.iter_transaction_hash_refs()] - - share_info, gentx, other_tx_hashes2, get_share = self.generate_transaction(tracker, self.share_info['share_data'], self.header['bits'].target, self.share_info['timestamp'], self.share_info['bits'].target, self.contents['ref_merkle_link'], [(h, None) for h in other_tx_hashes], self.net, last_txout_nonce=self.contents['last_txout_nonce']) +# print self + + share_info, gentx, other_tx_hashes2, get_share = self.generate_transaction( + tracker, + self.share_info['share_data'], + self.header['bits'].target, + self.share_info['timestamp'], + self.share_info['bits'].target, + self.contents['ref_merkle_link'], + [(h, None) for h in other_tx_hashes], + self.net, + last_txout_nonce=self.contents['last_txout_nonce'], + base_subsidy=None + ) + assert other_tx_hashes2 == other_tx_hashes if share_info != self.share_info: + print share_info, self.share_info raise ValueError('share_info invalid') if bitcoin_data.hash256(bitcoin_data.tx_type.pack(gentx)) != self.gentx_hash: raise ValueError('''gentx doesn't match hash_link''') @@ -365,7 +433,7 @@ class Share(object): other_txs = self._get_other_txs(tracker, known_txs) if other_txs is None: return None # not all txs present - return dict(header=self.header, txs=[self.check(tracker)] + other_txs) + return dict(header=self.header, txs=[self.check(tracker)] + other_txs, signature='') class WeightsSkipList(forest.TrackerSkipList): @@ -570,18 +638,33 @@ def get_stale_counts(tracker, share_hash, lookbehind, rates=False): def get_user_stale_props(tracker, share_hash, lookbehind): res = {} for share in tracker.get_chain(share_hash, lookbehind - 1): - stale, total = res.get(share.share_data['pubkey_hash'], (0, 0)) + stale, total = res.get(share.share_data['pubkey'], (0, 0)) total += 1 if share.share_data['stale_info'] is not None: stale += 1 total += 1 - res[share.share_data['pubkey_hash']] = stale, total - return dict((pubkey_hash, stale/total) for pubkey_hash, (stale, total) in res.iteritems()) + res[share.share_data['pubkey']] = stale, total + return dict((pubkey, stale/total) for pubkey, (stale, total) in res.iteritems()) + +def calculate_payout(weight, total_weight, subsidy): + global minout + + payout = (subsidy - 3*minout) * weight//total_weight + + if payout < minout: + payout = 0 + + return payout def get_expected_payouts(tracker, best_share_hash, block_target, subsidy, net): + weights, total_weight, donation_weight = tracker.get_cumulative_weights(best_share_hash, min(tracker.get_height(best_share_hash), net.REAL_CHAIN_LENGTH), 65535*net.SPREAD*bitcoin_data.target_to_average_attempts(block_target)) - res = dict((script, subsidy*weight//total_weight) for script, weight in weights.iteritems()) + + #res = dict((script, subsidy*weight//total_weight) for script, weight in weights.iteritems()) + + res = dict((script, calculate_payout(weight, total_weight, subsidy)) for script, weight in weights.iteritems()) res[DONATION_SCRIPT] = res.get(DONATION_SCRIPT, 0) + subsidy - sum(res.itervalues()) + return res def get_desired_version_counts(tracker, best_share_hash, dist): diff --git a/p2pool/main.py b/p2pool/main.py index 0e2c743..efc4248 100644 --- a/p2pool/main.py +++ b/p2pool/main.py @@ -14,7 +14,7 @@ import urlparse if '--iocp' in sys.argv: from twisted.internet import iocpreactor iocpreactor.install() -from twisted.internet import defer, reactor, protocol, task, tcp +from twisted.internet import defer, reactor, protocol, task from twisted.web import server from twisted.python import log from nattraverso import portmapper, ipdiscover @@ -69,32 +69,33 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint): factory = yield connect_p2p() print 'Determining payout address...' - if args.pubkey_hash is None: - address_path = os.path.join(datadir_path, 'cached_payout_address') - - if os.path.exists(address_path): - with open(address_path, 'rb') as f: - address = f.read().strip('\r\n') - print ' Loaded cached address: %s...' % (address,) - else: - address = None - - if address is not None: - res = yield deferral.retry('Error validating cached address:', 5)(lambda: bitcoind.rpc_validateaddress(address))() + pubkey_path = os.path.join(datadir_path, 'cached_payout_pubkey') + + if os.path.exists(pubkey_path): + with open(pubkey_path, 'rb') as f: + pubkey = f.read().strip('\r\n') + print ' Loaded cached pubkey, payout address: %s...' % (bitcoin_data.pubkey_to_address(pubkey.decode('hex'), net.PARENT),) + else: + pubkey = None + + if pubkey is not None: + res = yield deferral.retry('Error validating cached pubkey:', 5)(lambda: bitcoind.rpc_validatepubkey(pubkey))() if not res['isvalid'] or not res['ismine']: - print ' Cached address is either invalid or not controlled by local bitcoind!' + print ' Cached pubkey is either invalid or not controlled by local bitcoind!' address = None + + if pubkey is None: + print ' Getting payout pubkey from bitcoind...' + pubkey = yield deferral.retry('Error getting payout pubkey from bitcoind:', 5)(lambda: bitcoind.rpc_getnewpubkey('p2pool'))() - if address is None: - print ' Getting payout address from bitcoind...' - address = yield deferral.retry('Error getting payout address from bitcoind:', 5)(lambda: bitcoind.rpc_getaccountaddress('p2pool'))() - - with open(address_path, 'wb') as f: - f.write(address) - - my_pubkey_hash = bitcoin_data.address_to_pubkey_hash(address, net.PARENT) - else: - my_pubkey_hash = args.pubkey_hash + with open(pubkey_path, 'wb') as f: + f.write(pubkey) + + my_pubkey = pubkey.decode('hex') + + address = bitcoin_data.pubkey_to_address(my_pubkey, net.PARENT) + + my_pubkey_hash = bitcoin_data.address_to_pubkey_hash(address, net.PARENT) print ' ...success! Payout address:', bitcoin_data.pubkey_hash_to_address(my_pubkey_hash, net.PARENT) print @@ -212,7 +213,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint): print 'Listening for workers on %r port %i...' % (worker_endpoint[0], worker_endpoint[1]) - wb = work.WorkerBridge(node, my_pubkey_hash, args.donation_percentage, merged_urls, args.worker_fee) + wb = work.WorkerBridge(node, my_pubkey, args.donation_percentage, merged_urls, args.worker_fee) web_root = web.get_web_root(wb, datadir_path, bitcoind_warning_var) caching_wb = worker_interface.CachingWorkerBridge(wb) worker_interface.WorkerInterface(caching_wb).attach_to(web_root, get_handler=lambda request: request.redirect('/static/')) @@ -327,7 +328,7 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint): shares, stale_orphan_shares, stale_doa_shares, math.format_binomial_conf(stale_orphan_shares + stale_doa_shares, shares, 0.95), math.format_binomial_conf(stale_orphan_shares + stale_doa_shares, shares, 0.95, lambda x: (1 - x)/(1 - stale_prop)), - node.get_current_txouts().get(bitcoin_data.pubkey_hash_to_script2(my_pubkey_hash), 0)*1e-8, net.PARENT.SYMBOL, + node.get_current_txouts().get(bitcoin_data.pubkey_to_script2(my_pubkey), 0) * 1e-6, net.PARENT.SYMBOL, ) this_str += '\n Pool: %sH/s Stale rate: %.1f%% Expected time to block: %s' % ( math.format(int(real_att_s)), @@ -355,11 +356,6 @@ def main(args, net, datadir_path, merged_urls, worker_endpoint): log.err(None, 'Fatal error:') def run(): - if not hasattr(tcp.Client, 'abortConnection'): - print "Twisted doesn't have abortConnection! Upgrade to a newer version of Twisted to avoid memory leaks!" - print 'Pausing for 3 seconds...' - time.sleep(3) - realnets = dict((name, net) for name, net in networks.nets.iteritems() if '_testnet' not in name) parser = fixargparse.FixedArgumentParser(description='p2pool (version %s)' % (p2pool.__version__,), fromfile_prefix_chars='@') @@ -512,15 +508,7 @@ def run(): else: addr, port = args.worker_endpoint.rsplit(':', 1) worker_endpoint = addr, int(port) - - if args.address is not None: - try: - args.pubkey_hash = bitcoin_data.address_to_pubkey_hash(args.address, net.PARENT) - except Exception, e: - parser.error('error parsing address: ' + repr(e)) - else: - args.pubkey_hash = None - + def separate_url(url): s = urlparse.urlsplit(url) if '@' not in s.netloc: diff --git a/p2pool/networks.py b/p2pool/networks.py index 892da6a..78439e3 100644 --- a/p2pool/networks.py +++ b/p2pool/networks.py @@ -8,117 +8,42 @@ from p2pool.util import math # changes can be done by changing one, then the other nets = dict( - bitcoin=math.Object( - PARENT=networks.nets['bitcoin'], + novacoin=math.Object( + PARENT=networks.nets['novacoin'], SHARE_PERIOD=10, # seconds CHAIN_LENGTH=24*60*60//10, # shares REAL_CHAIN_LENGTH=24*60*60//10, # shares TARGET_LOOKBEHIND=200, # shares SPREAD=3, # blocks - IDENTIFIER='fc70035c7a81bc6f'.decode('hex'), - PREFIX='2472ef181efcd37b'.decode('hex'), - P2P_PORT=9333, - MIN_TARGET=0, - MAX_TARGET=2**256//2**32 - 1, - PERSIST=True, - WORKER_PORT=9332, - BOOTSTRAP_ADDRS='forre.st vps.forre.st 74.220.242.6:9334 93.97.192.93 66.90.73.83 67.83.108.0 219.84.64.174 24.167.17.248 109.74.195.142 83.211.86.49 94.23.34.145 168.7.116.243 94.174.40.189:9344 89.79.79.195 portals94.ns01.us'.split(' '), - ANNOUNCE_CHANNEL='#p2pool', - VERSION_CHECK=lambda v: 50700 <= v < 60000 or 60010 <= v < 60100 or 60400 <= v, - ), - bitcoin_testnet=math.Object( - PARENT=networks.nets['bitcoin_testnet'], - SHARE_PERIOD=10, # seconds - CHAIN_LENGTH=60*60//10, # shares - REAL_CHAIN_LENGTH=60*60//10, # shares - TARGET_LOOKBEHIND=200, # shares - SPREAD=3, # blocks - IDENTIFIER='5fc2be2d4f0d6bfb'.decode('hex'), - PREFIX='3f6057a15036f441'.decode('hex'), - P2P_PORT=19333, - MIN_TARGET=0, - MAX_TARGET=2**256//2**32 - 1, - PERSIST=False, - WORKER_PORT=19332, - BOOTSTRAP_ADDRS='forre.st vps.forre.st liteco.in'.split(' '), - ANNOUNCE_CHANNEL='#p2pool-alt', - VERSION_CHECK=lambda v: 50700 <= v < 60000 or 60010 <= v < 60100 or 60400 <= v, - ), - - litecoin=math.Object( - PARENT=networks.nets['litecoin'], - SHARE_PERIOD=10, # seconds - CHAIN_LENGTH=24*60*60//10, # shares - REAL_CHAIN_LENGTH=24*60*60//10, # shares - TARGET_LOOKBEHIND=200, # shares - SPREAD=12, # blocks - IDENTIFIER='e037d5b8c6923410'.decode('hex'), - PREFIX='7208c1a53ef629b0'.decode('hex'), - P2P_PORT=9338, + IDENTIFIER='e037d5b8c6923510'.decode('hex'), + PREFIX='7208c1a53ef649b0'.decode('hex'), + P2P_PORT=9777, MIN_TARGET=0, MAX_TARGET=2**256//2**20 - 1, PERSIST=True, - WORKER_PORT=9327, - BOOTSTRAP_ADDRS='forre.st vps.forre.st 199.255.95.94 75.12.89.18 181.28.244.151 83.142.189.132 66.90.82.155:11332 201.57.241.77 80.222.255.91 142.68.214.29 24.52.247.82 72.230.179.177 94.127.200.29 200.204.161.215 91.121.9.7 91.235.254.37 198.154.98.195 178.79.136.10'.split(' '), + WORKER_PORT=8336, + BOOTSTRAP_ADDRS='188.120.239.144'.split(' '), ANNOUNCE_CHANNEL='#p2pool-alt', VERSION_CHECK=lambda v: True, ), - litecoin_testnet=math.Object( - PARENT=networks.nets['litecoin_testnet'], + novacoin_testnet=math.Object( + PARENT=networks.nets['novacoin_testnet'], SHARE_PERIOD=3, # seconds CHAIN_LENGTH=20*60//3, # shares REAL_CHAIN_LENGTH=20*60//3, # shares TARGET_LOOKBEHIND=200, # shares SPREAD=12, # blocks - IDENTIFIER='cca5e24ec6408b1e'.decode('hex'), - PREFIX='ad9614f6466a39cf'.decode('hex'), - P2P_PORT=19338, + IDENTIFIER='e037d5b8c7923510'.decode('hex'), + PREFIX='7208c1a54ef649b0'.decode('hex'), + P2P_PORT=19777, MIN_TARGET=0, - MAX_TARGET=2**256//2000 - 1, - PERSIST=False, - WORKER_PORT=19327, - BOOTSTRAP_ADDRS='forre.st vps.forre.st'.split(' '), - ANNOUNCE_CHANNEL='#p2pool-alt', - VERSION_CHECK=lambda v: True, - ), - - terracoin=math.Object( - PARENT=networks.nets['terracoin'], - SHARE_PERIOD=30, # seconds - CHAIN_LENGTH=24*60*60//30, # shares - REAL_CHAIN_LENGTH=24*60*60//30, # shares - TARGET_LOOKBEHIND=200, # shares - SPREAD=15, # blocks - IDENTIFIER='a41b2356a1b7d35e'.decode('hex'), - PREFIX='5623b62178d2b8a3'.decode('hex'), - P2P_PORT=9323, - MIN_TARGET=0, - MAX_TARGET=2**256//2**32 - 1, - PERSIST=True, - WORKER_PORT=9322, - BOOTSTRAP_ADDRS='seed1.p2pool.terracoin.org seed2.p2pool.terracoin.org forre.st vps.forre.st 93.97.192.93 66.90.73.83 67.83.108.0 219.84.64.174 24.167.17.248 109.74.195.142 83.211.86.49 94.23.34.145 168.7.116.243 94.174.40.189:9344 89.79.79.195 portals94.ns01.us'.split(' '), - ANNOUNCE_CHANNEL='#p2pool-alt', - VERSION_CHECK=lambda v: True, - ), - terracoin_testnet=math.Object( - PARENT=networks.nets['terracoin_testnet'], - SHARE_PERIOD=30, # seconds - CHAIN_LENGTH=60*60//30, # shares - REAL_CHAIN_LENGTH=60*60//30, # shares - TARGET_LOOKBEHIND=200, # shares - SPREAD=15, # blocks - IDENTIFIER='b41b2356a5b7d35d'.decode('hex'), - PREFIX='1623b92172d2b8a2'.decode('hex'), - P2P_PORT=19323, - MIN_TARGET=0, - MAX_TARGET=2**256//2**32 - 1, + MAX_TARGET=2**256//2**20 - 1, PERSIST=False, - WORKER_PORT=19322, - BOOTSTRAP_ADDRS='seed1.p2pool.terracoin.org seed2.p2pool.terracoin.org forre.st vps.forre.st'.split(' '), + WORKER_PORT=18336, + BOOTSTRAP_ADDRS='188.120.239.144'.split(' '), ANNOUNCE_CHANNEL='#p2pool-alt', VERSION_CHECK=lambda v: True, ), - ) for net_name, net in nets.iteritems(): net.NAME = net_name diff --git a/p2pool/node.py b/p2pool/node.py index 1c98866..9b21768 100644 --- a/p2pool/node.py +++ b/p2pool/node.py @@ -195,10 +195,10 @@ class Node(object): if (self.best_block_header.value is None or ( new_header['previous_block'] == bitcoind_best_block and - bitcoin_data.hash256(bitcoin_data.block_header_type.pack(self.best_block_header.value)) == bitcoind_best_block + self.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(self.best_block_header.value)) == bitcoind_best_block ) # new is child of current and previous is current or ( - bitcoin_data.hash256(bitcoin_data.block_header_type.pack(new_header)) == bitcoind_best_block and + self.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(new_header)) == bitcoind_best_block and self.best_block_header.value['previous_block'] != bitcoind_best_block )): # new is current and previous is not a child of current self.best_block_header.set(new_header) @@ -252,9 +252,11 @@ class Node(object): @self.tracker.verified.added.watch def _(share): + if share.timestamp < share.min_header['timestamp']: + return if not (share.pow_hash <= share.header['bits'].target): return - + block = share.as_block(self.tracker, self.known_txs_var.value) if block is None: print >>sys.stderr, 'GOT INCOMPLETE BLOCK FROM PEER! %s bitcoin: %s%064x' % (p2pool_data.format_hash(share.hash), self.net.PARENT.BLOCK_EXPLORER_URL_PREFIX, share.header_hash) diff --git a/p2pool/test/test_node.py b/p2pool/test/test_node.py index c3f7719..5b39542 100644 --- a/p2pool/test/test_node.py +++ b/p2pool/test/test_node.py @@ -143,7 +143,7 @@ class MiniNode(object): self.n.p2p_node = node.P2PNode(self.n, port=0, max_incoming_conns=1000000, addr_store={}, connect_addrs=[('127.0.0.1', peer_port) for peer_port in peer_ports]) self.n.p2p_node.start() - wb = work.WorkerBridge(node=self.n, my_pubkey_hash=random.randrange(2**160), donation_percentage=random.uniform(0, 10), merged_urls=merged_urls, worker_fee=3) + wb = work.WorkerBridge(node=self.n, my_pubkey=random.randrange(2**520), donation_percentage=random.uniform(0, 10), merged_urls=merged_urls, worker_fee=3) self.wb = wb web_root = resource.Resource() worker_interface.WorkerInterface(wb).attach_to(web_root) @@ -170,7 +170,7 @@ class Test(unittest.TestCase): n = node.Node(bitd, bitd, [], [], mynet) yield n.start() - wb = work.WorkerBridge(node=n, my_pubkey_hash=42, donation_percentage=2, merged_urls=[('http://127.0.0.1:%i' % (mm_port.getHost().port,), '')], worker_fee=3) + wb = work.WorkerBridge(node=n, my_pubkey=42, donation_percentage=2, merged_urls=[('http://127.0.0.1:%i' % (mm_port.getHost().port,), '')], worker_fee=3) web_root = resource.Resource() worker_interface.WorkerInterface(wb).attach_to(web_root) port = reactor.listenTCP(0, server.Site(web_root)) diff --git a/p2pool/util/jsonrpc.py b/p2pool/util/jsonrpc.py index d810ada..bf11813 100644 --- a/p2pool/util/jsonrpc.py +++ b/p2pool/util/jsonrpc.py @@ -129,7 +129,7 @@ def _http_do(url, headers, timeout, method, params): if resp['id'] != id_: raise ValueError('invalid id') - if 'error' in resp and resp['error'] is not None: + if 'error' in resp and resp['error'] is not None and resp['error']['code'] != -100: raise Error_for_code(resp['error']['code'])(resp['error']['message'], resp['error'].get('data', None)) defer.returnValue(resp['result']) HTTPProxy = lambda url, headers={}, timeout=5: Proxy(lambda method, params: _http_do(url, headers, timeout, method, params)) diff --git a/p2pool/web.py b/p2pool/web.py index c401e82..cc80a07 100644 --- a/p2pool/web.py +++ b/p2pool/web.py @@ -176,7 +176,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve uptime=time.time() - start_time, attempts_to_share=bitcoin_data.target_to_average_attempts(node.tracker.items[node.best_share_var.value].max_target), attempts_to_block=bitcoin_data.target_to_average_attempts(node.bitcoind_work.value['bits'].target), - block_value=node.bitcoind_work.value['subsidy']*1e-8, + block_value=node.bitcoind_work.value['subsidy']*1e-6, warnings=p2pool_data.get_warnings(node.tracker, node.best_share_var.value, node.net, bitcoind_warning_var.value, node.bitcoind_work.value), donation_proportion=wb.donation_percentage/100, ) @@ -195,13 +195,13 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve request.setHeader('Access-Control-Allow-Origin', '*') res = yield self.func(*self.args) defer.returnValue(json.dumps(res) if self.mime_type == 'application/json' else res) - + def decent_height(): return min(node.tracker.get_height(node.best_share_var.value), 720) web_root.putChild('rate', WebInterface(lambda: p2pool_data.get_pool_attempts_per_second(node.tracker, node.best_share_var.value, decent_height())/(1-p2pool_data.get_average_stale_prop(node.tracker, node.best_share_var.value, decent_height())))) web_root.putChild('difficulty', WebInterface(lambda: bitcoin_data.target_to_difficulty(node.tracker.items[node.best_share_var.value].max_target))) web_root.putChild('users', WebInterface(get_users)) - web_root.putChild('user_stales', WebInterface(lambda: dict((bitcoin_data.pubkey_hash_to_address(ph, node.net.PARENT), prop) for ph, prop in + web_root.putChild('user_stales', WebInterface(lambda: dict((bitcoin_data.pubkey_to_address(ph, node.net.PARENT), prop) for ph, prop in p2pool_data.get_user_stale_props(node.tracker, node.best_share_var.value, node.tracker.get_height(node.best_share_var.value)).iteritems()))) web_root.putChild('fee', WebInterface(lambda: wb.worker_fee)) web_root.putChild('current_payouts', WebInterface(lambda: dict((bitcoin_data.script2_to_address(script, node.net.PARENT), value/1e8) for script, value in node.get_current_txouts().iteritems()))) @@ -221,7 +221,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve ]) )))) web_root.putChild('peer_versions', WebInterface(lambda: dict(('%s:%i' % peer.addr, peer.other_sub_version) for peer in node.p2p_node.peers.itervalues()))) - web_root.putChild('payout_addr', WebInterface(lambda: bitcoin_data.pubkey_hash_to_address(wb.my_pubkey_hash, node.net.PARENT))) + web_root.putChild('payout_addr', WebInterface(lambda: bitcoin_data.pubkey_to_address(wb.my_pubkey, node.net.PARENT))) web_root.putChild('recent_blocks', WebInterface(lambda: [dict( ts=s.timestamp, hash='%064x' % s.header_hash, @@ -262,14 +262,14 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve shares=shares, stale_shares=stale_orphan_shares + stale_doa_shares, stale_shares_breakdown=dict(orphan=stale_orphan_shares, doa=stale_doa_shares), - current_payout=node.get_current_txouts().get(bitcoin_data.pubkey_hash_to_script2(wb.my_pubkey_hash), 0)*1e-8, + current_payout=node.get_current_txouts().get(bitcoin_data.pubkey_to_script2(wb.my_pubkey), 0)*1e-6, peers=dict( incoming=sum(1 for peer in node.p2p_node.peers.itervalues() if peer.incoming), outgoing=sum(1 for peer in node.p2p_node.peers.itervalues() if not peer.incoming), ), attempts_to_share=bitcoin_data.target_to_average_attempts(node.tracker.items[node.best_share_var.value].max_target), attempts_to_block=bitcoin_data.target_to_average_attempts(node.bitcoind_work.value['bits'].target), - block_value=node.bitcoind_work.value['subsidy']*1e-8, + block_value=node.bitcoind_work.value['subsidy']*1e-6, )) with open(os.path.join(datadir_path, 'stats'), 'wb') as f: @@ -316,7 +316,7 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve gentx=dict( hash='%064x' % share.gentx_hash, coinbase=share.share_data['coinbase'].ljust(2, '\x00').encode('hex'), - value=share.share_data['subsidy']*1e-8, + value=share.share_data['subsidy']*1e-6, ), txn_count_range=[len(share.other_txs), len(share.other_txs)] if share.other_txs is not None else 1 if len(share.merkle_link['branch']) == 0 else [2**len(share.merkle_link['branch'])//2+1, 2**len(share.merkle_link['branch'])], ), @@ -428,10 +428,10 @@ def get_web_root(wb, datadir_path, bitcoind_warning_var, stop_event=variable.Eve hd.datastreams['pool_rates'].add_datum(t, pool_rates) current_txouts = node.get_current_txouts() - hd.datastreams['current_payout'].add_datum(t, current_txouts.get(bitcoin_data.pubkey_hash_to_script2(wb.my_pubkey_hash), 0)*1e-8) + hd.datastreams['current_payout'].add_datum(t, current_txouts.get(bitcoin_data.pubkey_to_script2(wb.my_pubkey), 0)*1e-6) miner_hash_rates, miner_dead_hash_rates = get_local_rates() current_txouts_by_address = dict((bitcoin_data.script2_to_address(script, node.net.PARENT), amount) for script, amount in current_txouts.iteritems()) - hd.datastreams['current_payouts'].add_datum(t, dict((user, current_txouts_by_address[user]*1e-8) for user in miner_hash_rates if user in current_txouts_by_address)) + hd.datastreams['current_payouts'].add_datum(t, dict((user, current_txouts_by_address[user]*1e-6) for user in miner_hash_rates if user in current_txouts_by_address)) hd.datastreams['incoming_peers'].add_datum(t, sum(1 for peer in node.p2p_node.peers.itervalues() if peer.incoming)) hd.datastreams['outgoing_peers'].add_datum(t, sum(1 for peer in node.p2p_node.peers.itervalues() if not peer.incoming)) diff --git a/p2pool/work.py b/p2pool/work.py index 318c1d9..2126839 100644 --- a/p2pool/work.py +++ b/p2pool/work.py @@ -16,12 +16,12 @@ import p2pool, p2pool.data as p2pool_data class WorkerBridge(worker_interface.WorkerBridge): COINBASE_NONCE_LENGTH = 4 - def __init__(self, node, my_pubkey_hash, donation_percentage, merged_urls, worker_fee): + def __init__(self, node, my_pubkey, donation_percentage, merged_urls, worker_fee): worker_interface.WorkerBridge.__init__(self) self.recent_shares_ts_work = [] self.node = node - self.my_pubkey_hash = my_pubkey_hash + self.my_pubkey = my_pubkey self.donation_percentage = donation_percentage self.worker_fee = worker_fee @@ -80,16 +80,20 @@ class WorkerBridge(worker_interface.WorkerBridge): # COMBINE WORK + self.current_work = variable.Variable(None) def compute_work(): t = self.node.bitcoind_work.value bb = self.node.best_block_header.value + + subsidy = self.node.net.PARENT.SUBSIDY_FUNC(bb['bits'].target) + if bb is not None and bb['previous_block'] == t['previous_block'] and self.node.net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(bb)) <= t['bits'].target: print 'Skipping from block %x to block %x!' % (bb['previous_block'], - bitcoin_data.hash256(bitcoin_data.block_header_type.pack(bb))) + self.node.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(bb))) t = dict( version=bb['version'], - previous_block=bitcoin_data.hash256(bitcoin_data.block_header_type.pack(bb)), + previous_block=self.node.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(bb)), bits=bb['bits'], # not always true coinbaseflags='', height=t['height'] + 1, @@ -97,7 +101,7 @@ class WorkerBridge(worker_interface.WorkerBridge): transactions=[], transaction_fees=[], merkle_link=bitcoin_data.calculate_merkle_link([None], 0), - subsidy=self.node.net.PARENT.SUBSIDY_FUNC(self.node.bitcoind_work.value['height']), + subsidy=subsidy, last_update=self.node.bitcoind_work.value['last_update'], ) @@ -150,21 +154,15 @@ class WorkerBridge(worker_interface.WorkerBridge): except: pass - if random.uniform(0, 100) < self.worker_fee: - pubkey_hash = self.my_pubkey_hash - else: - try: - pubkey_hash = bitcoin_data.address_to_pubkey_hash(user, self.node.net.PARENT) - except: # XXX blah - pubkey_hash = self.my_pubkey_hash + pubkey = self.my_pubkey - return user, pubkey_hash, desired_share_target, desired_pseudoshare_target + return user, pubkey, desired_share_target, desired_pseudoshare_target def preprocess_request(self, user): - user, pubkey_hash, desired_share_target, desired_pseudoshare_target = self.get_user_details(user) - return pubkey_hash, desired_share_target, desired_pseudoshare_target + user, pubkey, desired_share_target, desired_pseudoshare_target = self.get_user_details(user) + return pubkey, desired_share_target, desired_pseudoshare_target - def get_work(self, pubkey_hash, desired_share_target, desired_pseudoshare_target): + def get_work(self, pubkey, desired_share_target, desired_pseudoshare_target): if (self.node.p2p_node is None or len(self.node.p2p_node.peers) == 0) and self.node.net.PERSIST: raise jsonrpc.Error_for_code(-12345)(u'p2pool is not connected to any peers') if self.node.best_share_var.value is None and self.node.net.PERSIST: @@ -208,6 +206,8 @@ class WorkerBridge(worker_interface.WorkerBridge): share_type = previous_share_type if True: + subsidy = self.node.net.PARENT.SUBSIDY_FUNC(self.current_work.value['bits'].target) + share_info, gentx, other_transaction_hashes, get_share = share_type.generate_transaction( tracker=self.node.tracker, share_data=dict( @@ -217,7 +217,7 @@ class WorkerBridge(worker_interface.WorkerBridge): ] + ([mm_data] if mm_data else []) + [ ]) + self.current_work.value['coinbaseflags'])[:100], nonce=random.randrange(2**32), - pubkey_hash=pubkey_hash, + pubkey=pubkey, subsidy=self.current_work.value['subsidy'], donation=math.perfect_round(65535*self.donation_percentage/100), stale_info=(lambda (orphans, doas), total, (orphans_recorded_in_chain, doas_recorded_in_chain): @@ -234,7 +234,7 @@ class WorkerBridge(worker_interface.WorkerBridge): desired_other_transaction_hashes_and_fees=zip(tx_hashes, self.current_work.value['transaction_fees']), net=self.node.net, known_txs=tx_map, - base_subsidy=self.node.net.PARENT.SUBSIDY_FUNC(self.current_work.value['height']), + base_subsidy=subsidy ) packed_gentx = bitcoin_data.tx_type.pack(gentx) @@ -262,7 +262,7 @@ class WorkerBridge(worker_interface.WorkerBridge): print 'New work for worker! Difficulty: %.06f Share difficulty: %.06f Total block value: %.6f %s including %i transactions' % ( bitcoin_data.target_to_difficulty(target), bitcoin_data.target_to_difficulty(share_info['bits'].target), - self.current_work.value['subsidy']*1e-8, self.node.net.PARENT.SYMBOL, + self.current_work.value['subsidy']*1e-6, self.node.net.PARENT.SYMBOL, len(self.current_work.value['transactions']), ) @@ -272,7 +272,7 @@ class WorkerBridge(worker_interface.WorkerBridge): merkle_link=merkle_link, coinb1=packed_gentx[:-4-4], coinb2=packed_gentx[-4:], - timestamp=self.current_work.value['time'], + timestamp=gentx['timestamp'], bits=self.current_work.value['bits'], share_target=target, ) @@ -284,11 +284,26 @@ class WorkerBridge(worker_interface.WorkerBridge): new_packed_gentx = packed_gentx[:-4-4] + coinbase_nonce + packed_gentx[-4:] if coinbase_nonce != '\0'*self.COINBASE_NONCE_LENGTH else packed_gentx new_gentx = bitcoin_data.tx_type.unpack(new_packed_gentx) if coinbase_nonce != '\0'*self.COINBASE_NONCE_LENGTH else gentx - header_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(header)) + header_hash = self.node.net.PARENT.BLOCKHASH_FUNC(bitcoin_data.block_header_type.pack(header)) pow_hash = self.node.net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(header)) try: + if header['timestamp'] > new_gentx['timestamp'] + 3600: + print + print header['timestamp'], '>', new_gentx['timestamp'] + 3600 + print 'Coinbase timestamp is too early!' + print + + return + + if header['timestamp'] < new_gentx['timestamp']: + print + print header['timestamp'], '<', new_gentx['timestamp'] + print 'Block header timestamp is before coinbase timestamp!' + print + return + if pow_hash <= header['bits'].target or p2pool.DEBUG: - helper.submit_block(dict(header=header, txs=[new_gentx] + other_transactions), False, self.node.factory, self.node.bitcoind, self.node.bitcoind_work, self.node.net) + helper.submit_block(dict(header=header, txs=[new_gentx] + other_transactions, signature=''), False, self.node.factory, self.node.bitcoind, self.node.bitcoind_work, self.node.net) if pow_hash <= header['bits'].target: print print 'GOT BLOCK FROM MINER! Passing to bitcoind! %s%064x' % (self.node.net.PARENT.BLOCK_EXPLORER_URL_PREFIX, header_hash)