import StringIO
import argparse
import base64
+import json
import os
import random
import sys
import traceback
import urlparse
+try:
+ from twisted.internet import iocpreactor
+ iocpreactor.install()
+except:
+ pass
+else:
+ print 'Using IOCP reactor!'
from twisted.internet import defer, reactor, protocol, task
from twisted.web import server
from twisted.python import log
if not (yield net.PARENT.RPC_CHECK)(bitcoind):
print >>sys.stderr, " Check failed! Make sure that you're connected to the right bitcoind with --bitcoind-rpc-port!"
raise deferral.RetrySilentlyException()
- v = (yield bitcoind.rpc_getinfo())['version']
temp_work = yield getwork(bitcoind)
- if not net.VERSION_CHECK((v//10000, v//100%100, v%100), temp_work):
+ if not net.VERSION_CHECK((yield bitcoind.rpc_getinfo())['version'], temp_work):
print >>sys.stderr, ' Bitcoin version too old! BIP16 support required! Upgrade to 0.6.0rc4 or greater!'
raise deferral.RetrySilentlyException()
defer.returnValue(temp_work)
shared_share_hashes = set()
ss = p2pool_data.ShareStore(os.path.join(datadir_path, 'shares.'), net)
known_verified = set()
- recent_blocks = []
print "Loading shares..."
for i, (mode, contents) in enumerate(ss.get_shares()):
if mode == 'share':
break
shares.append(share)
print 'Sending %i shares to %s:%i' % (len(shares), peer.addr[0], peer.addr[1])
- peer.sendShares(shares)
+ return shares
+
+ @deferral.retry('Error submitting primary block: (will retry)', 10, 10)
+ def submit_block_p2p(block):
+ if factory.conn.value is None:
+ print >>sys.stderr, 'No bitcoind connection when block submittal attempted! %s%32x' % (net.PARENT.BLOCK_EXPLORER_URL_PREFIX, header_hash)
+ raise deferral.RetrySilentlyException()
+ factory.conn.value.send_block(block=block)
@deferral.retry('Error submitting block: (will retry)', 10, 10)
@defer.inlineCallbacks
- def submit_block(block, ignore_failure):
+ def submit_block_rpc(block, ignore_failure):
success = yield bitcoind.rpc_getmemorypool(bitcoin_data.block_type.pack(block).encode('hex'))
success_expected = net.PARENT.POW_FUNC(bitcoin_data.block_header_type.pack(block['header'])) <= block['header']['bits'].target
if (not success and success_expected and not ignore_failure) or (success and not success_expected):
print >>sys.stderr, 'Block submittal result: %s Expected: %s' % (result, expected_result)
+ def submit_block(block, ignore_failure):
+ submit_block_p2p(block)
+ submit_block_rpc(block, ignore_failure)
+
@tracker.verified.added.watch
def _(share):
if share.pow_hash <= share.header['bits'].target:
print
print 'GOT BLOCK FROM PEER! Passing to bitcoind! %s bitcoin: %s%064x' % (p2pool_data.format_hash(share.hash), net.PARENT.BLOCK_EXPLORER_URL_PREFIX, share.header_hash)
print
- recent_blocks.append(dict(ts=share.timestamp, hash='%064x' % (share.header_hash,)))
+ if current_work.value['previous_block'] in [share.header['previous_block'], share.header_hash]:
+ broadcast_share(share.hash)
print 'Joining p2pool network using port %i...' % (args.p2pool_port,)
defer.returnValue(((yield reactor.resolve(x)), net.P2P_PORT))
addrs = {}
- if os.path.exists(os.path.join(datadir_path, 'addrs.txt')):
+ if os.path.exists(os.path.join(datadir_path, 'addrs')):
+ try:
+ with open(os.path.join(datadir_path, 'addrs'), 'rb') as f:
+ addrs.update(dict((tuple(k), v) for k, v in json.loads(f.read())))
+ except:
+ print >>sys.stderr, 'error parsing addrs'
+ elif os.path.exists(os.path.join(datadir_path, 'addrs.txt')):
try:
addrs.update(dict(eval(x) for x in open(os.path.join(datadir_path, 'addrs.txt'))))
except:
- print >>sys.stderr, "error reading addrs"
+ print >>sys.stderr, "error reading addrs.txt"
for addr_df in map(parse, net.BOOTSTRAP_ADDRS):
try:
addr = yield addr_df
)
p2p_node.start()
- task.LoopingCall(lambda: open(os.path.join(datadir_path, 'addrs.txt'), 'w').writelines(repr(x) + '\n' for x in p2p_node.addr_store.iteritems())).start(60)
+ def save_addrs():
+ with open(os.path.join(datadir_path, 'addrs'), 'wb') as f:
+ f.write(json.dumps(p2p_node.addr_store.items()))
+ task.LoopingCall(save_addrs).start(60)
- # send share when the chain changes to their chain
- def work_changed(new_work):
- #print 'Work changed:', new_work
+ def broadcast_share(share_hash):
shares = []
- for share in tracker.get_chain(new_work['best_share_hash'], min(5, tracker.get_height(new_work['best_share_hash']))):
+ for share in tracker.get_chain(share_hash, min(5, tracker.get_height(share_hash))):
if share.hash in shared_share_hashes:
break
shared_share_hashes.add(share.hash)
for peer in p2p_node.peers.itervalues():
peer.sendShares([share for share in shares if share.peer is not peer])
- current_work.changed.watch(work_changed)
+ # send share when the chain changes to their chain
+ current_work.changed.watch(lambda new_work: broadcast_share(new_work['best_share_hash']))
def save_shares():
for share in tracker.get_chain(current_work.value['best_share_hash'], min(tracker.get_height(current_work.value['best_share_hash']), 2*net.CHAIN_LENGTH)):
print 'Listening for workers on %r port %i...' % (worker_endpoint[0], worker_endpoint[1])
- if os.path.exists(os.path.join(datadir_path, 'vip_pass')):
- with open(os.path.join(datadir_path, 'vip_pass'), 'rb') as f:
- vip_pass = f.read().strip('\r\n')
- else:
- vip_pass = '%016x' % (random.randrange(2**64),)
- with open(os.path.join(datadir_path, 'vip_pass'), 'wb') as f:
- f.write(vip_pass)
- print ' Worker password:', vip_pass, '(only required for generating graphs)'
-
# setup worker logic
removed_unstales_var = variable.Variable((0, 0, 0))
'''Returns (orphans, doas), total, (orphans_recorded_in_chain, doas_recorded_in_chain)'''
my_shares = len(my_share_hashes)
my_doa_shares = len(my_doa_share_hashes)
- delta = tracker.verified.get_delta(current_work.value['best_share_hash'])
+ delta = tracker.verified.get_delta_to_last(current_work.value['best_share_hash'])
my_shares_in_chain = delta.my_count + removed_unstales_var.value[0]
my_doa_shares_in_chain = delta.my_doa_count + removed_doa_unstales_var.value
orphans_recorded_in_chain = delta.my_orphan_announce_count + removed_unstales_var.value[1]
pseudoshare_received = variable.Event()
+ share_received = variable.Event()
local_rate_monitor = math.RateMonitor(10*60)
class WorkerBridge(worker_interface.WorkerBridge):
self.new_work_event = current_work.changed
self.recent_shares_ts_work = []
- def preprocess_request(self, request):
+ def get_user_details(self, request):
user = request.getUser() if request.getUser() is not None else ''
desired_pseudoshare_target = None
except: # XXX blah
pubkey_hash = my_pubkey_hash
+ return user, pubkey_hash, desired_share_target, desired_pseudoshare_target
+
+ def preprocess_request(self, request):
+ user, pubkey_hash, desired_share_target, desired_pseudoshare_target = self.get_user_details(request)
return pubkey_hash, desired_share_target, desired_pseudoshare_target
def get_work(self, pubkey_hash, desired_share_target, desired_pseudoshare_target):
mm_data = ''
mm_later = []
- share_info, generate_tx = p2pool_data.Share.generate_transaction(
- tracker=tracker,
- share_data=dict(
- previous_share_hash=current_work.value['best_share_hash'],
- coinbase=(mm_data + current_work.value['coinbaseflags'])[:100],
- nonce=random.randrange(2**32),
- pubkey_hash=pubkey_hash,
- subsidy=current_work2.value['subsidy'],
- donation=math.perfect_round(65535*args.donation_percentage/100),
- stale_info=(lambda (orphans, doas), total, (orphans_recorded_in_chain, doas_recorded_in_chain):
- 253 if orphans > orphans_recorded_in_chain else
- 254 if doas > doas_recorded_in_chain else
- 0
- )(*get_stale_counts()),
- ),
- block_target=current_work.value['bits'].target,
- desired_timestamp=int(time.time() - current_work2.value['clock_offset']),
- desired_target=desired_share_target,
- net=net,
- )
+ if True:
+ share_info, generate_tx = p2pool_data.Share.generate_transaction(
+ tracker=tracker,
+ share_data=dict(
+ previous_share_hash=current_work.value['best_share_hash'],
+ coinbase=(mm_data + current_work.value['coinbaseflags'])[:100],
+ nonce=random.randrange(2**32),
+ pubkey_hash=pubkey_hash,
+ subsidy=current_work2.value['subsidy'],
+ donation=math.perfect_round(65535*args.donation_percentage/100),
+ stale_info=(lambda (orphans, doas), total, (orphans_recorded_in_chain, doas_recorded_in_chain):
+ 253 if orphans > orphans_recorded_in_chain else
+ 254 if doas > doas_recorded_in_chain else
+ 0
+ )(*get_stale_counts()),
+ desired_version=1,
+ ),
+ block_target=current_work.value['bits'].target,
+ desired_timestamp=int(time.time() - current_work2.value['clock_offset']),
+ desired_target=desired_share_target,
+ ref_merkle_link=dict(branch=[], index=0),
+ net=net,
+ )
target = net.PARENT.SANE_MAX_TARGET
if desired_pseudoshare_target is None:
received_header_hashes = set()
def got_response(header, request):
+ user, _, _, _ = self.get_user_details(request)
assert header['merkle_root'] == merkle_root
header_hash = bitcoin_data.hash256(bitcoin_data.block_header_type.pack(header))
print
print 'GOT BLOCK FROM MINER! Passing to bitcoind! %s%064x' % (net.PARENT.BLOCK_EXPLORER_URL_PREFIX, header_hash)
print
- recent_blocks.append(dict(ts=time.time(), hash='%064x' % (header_hash,)))
except:
log.err(None, 'Error while processing potential block:')
except:
log.err(None, 'Error while processing merged mining POW:')
- if pow_hash <= share_info['bits'].target:
+ if pow_hash <= share_info['bits'].target and header_hash not in received_header_hashes:
min_header = dict(header);del min_header['merkle_root']
hash_link = p2pool_data.prefix_to_hash_link(packed_generate_tx[:-32-4], p2pool_data.Share.gentx_before_refhash)
- share = p2pool_data.Share(net, None, min_header, share_info, hash_link=hash_link, merkle_link=merkle_link, other_txs=transactions[1:] if pow_hash <= header['bits'].target else None)
+ share = p2pool_data.Share(net, None, dict(
+ min_header=min_header, share_info=share_info, hash_link=hash_link,
+ ref_merkle_link=dict(branch=[], index=0),
+ ), merkle_link=merkle_link, other_txs=transactions[1:] if pow_hash <= header['bits'].target else None)
print 'GOT SHARE! %s %s prev %s age %.2fs%s' % (
request.getUser(),
shared_share_hashes.add(share.hash)
except:
log.err(None, 'Error forwarding block solution:')
+
+ share_received.happened(bitcoin_data.target_to_average_attempts(share.target), not on_time)
if pow_hash > target:
print 'Worker %s submitted share with hash > target:' % (request.getUser(),)
else:
received_header_hashes.add(header_hash)
- pseudoshare_received.happened(bitcoin_data.target_to_average_attempts(target), not on_time, request.getUser() if request.getPassword() == vip_pass else None)
+ pseudoshare_received.happened(bitcoin_data.target_to_average_attempts(target), not on_time, user)
self.recent_shares_ts_work.append((time.time(), bitcoin_data.target_to_average_attempts(target)))
while len(self.recent_shares_ts_work) > 50:
self.recent_shares_ts_work.pop(0)
- local_rate_monitor.add_datum(dict(work=bitcoin_data.target_to_average_attempts(target), dead=not on_time, user=request.getUser()))
+ local_rate_monitor.add_datum(dict(work=bitcoin_data.target_to_average_attempts(target), dead=not on_time, user=user))
return on_time
get_current_txouts = lambda: p2pool_data.get_expected_payouts(tracker, current_work.value['best_share_hash'], current_work.value['bits'].target, current_work2.value['subsidy'], net)
- web_root = web.get_web_root(tracker, current_work, current_work2, get_current_txouts, datadir_path, net, get_stale_counts, my_pubkey_hash, local_rate_monitor, args.worker_fee, p2p_node, my_share_hashes, recent_blocks, pseudoshare_received)
- worker_interface.WorkerInterface(WorkerBridge()).attach_to(web_root)
+ web_root = web.get_web_root(tracker, current_work, current_work2, get_current_txouts, datadir_path, net, get_stale_counts, my_pubkey_hash, local_rate_monitor, args.worker_fee, p2p_node, my_share_hashes, pseudoshare_received, share_received)
+ worker_interface.WorkerInterface(WorkerBridge()).attach_to(web_root, get_handler=lambda request: request.redirect('/static/'))
deferral.retry('Error binding to worker port:', traceback=False)(reactor.listenTCP)(worker_endpoint[1], server.Site(web_root), interface=worker_endpoint[0])
# done!
print 'Started successfully!'
+ print 'Go to http://127.0.0.1:%i/ to view graphs and statistics!' % (worker_endpoint[1],)
+ if args.donation_percentage > 0.51:
+ print '''Donating %.1f%% of work towards P2Pool's development. Thanks for the tip!''' % (args.donation_percentage,)
+ elif args.donation_percentage < 0.49:
+ print '''Donating %.1f%% of work towards P2Pool's development. Please donate to encourage further development of P2Pool!''' % (args.donation_percentage,)
+ else:
+ print '''Donating %.1f%% of work towards P2Pool's development. Thank you!''' % (args.donation_percentage,)
+ print 'You can increase this amount with --give-author argument! (or decrease it, if you must)'
print
nickname = 'p2pool%02i' % (random.randrange(100),)
channel = net.ANNOUNCE_CHANNEL
def lineReceived(self, line):
- print repr(line)
+ if p2pool.DEBUG:
+ print repr(line)
irc.IRCClient.lineReceived(self, line)
def signedOn(self):
irc.IRCClient.signedOn(self)
self.recent_messages = []
def _remember_message(self, message):
self.recent_messages.append(message)
- while len(self.recent_message) > 100:
+ while len(self.recent_messages) > 100:
self.recent_messages.pop(0)
def privmsg(self, user, channel, message):
if channel == self.channel:
math.format_dt(2**256 / current_work.value['bits'].target / real_att_s),
)
- desired_version_counts = p2pool_data.get_desired_version_counts(tracker, current_work.value['best_share_hash'], min(720, height))
- majority_desired_version = max(desired_version_counts, key=lambda k: desired_version_counts[k])
- if majority_desired_version not in [0]:
+ for warning in p2pool_data.get_warnings(tracker, current_work):
print >>sys.stderr, '#'*40
- print >>sys.stderr, '>>> WARNING: A MAJORITY OF SHARES CONTAIN A VOTE FOR AN UNSUPPORTED SHARE IMPLEMENTATION! (v%i with %i%% support)' % (
- majority_desired_version, 100*desired_version_counts[majority_desired_version]/sum(desired_version_counts.itervalues()))
- print >>sys.stderr, '>>> An upgrade is likely necessary. Check http://p2pool.forre.st/ for more information.'
+ print >>sys.stderr, '>>> Warning: ' + warning
print >>sys.stderr, '#'*40
if this_str != last_str or time.time() > last_time + 15:
help='call getauxblock on this url to get work for merged mining (example: http://ncuser:ncpass@127.0.0.1:10332/)',
type=str, action='append', default=[], dest='merged_urls')
parser.add_argument('--give-author', metavar='DONATION_PERCENTAGE',
- help='donate this percentage of work to author of p2pool (default: 0.5)',
+ help='donate this percentage of work towards the development of p2pool (default: 0.5)',
type=float, action='store', default=0.5, dest='donation_percentage')
parser.add_argument('--irc-announce',
help='announce any blocks found on irc://irc.freenode.net/#p2pool',