self.heads = {} # head hash -> tail_hash
self.tails = {} # tail hash -> set of head hashes
+
self.heights = {} # share_hash -> height_to, other_share_hash
- self.skips = {} # share_hash -> skip list
- self.id_generator = itertools.count()
- self.tails_by_id = {}
+ #self.id_generator = itertools.count()
+ #self.tails_by_id = {}
- self.get_nth_parent = skiplist.DistanceSkipList(self)
+ self.get_nth_parent_hash = skiplist.DistanceSkipList(self)
def add(self, share):
assert not isinstance(share, (int, long, type(None)))
assert self.test() is None
+ def get_height(self, share_hash):
+ height, work, last = self.get_height_work_and_last(share_hash)
+ return height
+
+ def get_work(self, share_hash):
+ height, work, last = self.get_height_work_and_last(share_hash)
+ return work
+
def get_height_and_last(self, share_hash):
+ height, work, last = self.get_height_work_and_last(share_hash)
+ return height, last
+
+ def get_height_work_and_last(self, share_hash):
assert isinstance(share_hash, (int, long, type(None)))
orig = share_hash
height = 0
+ work = 0
updates = []
while True:
if share_hash is None or share_hash not in self.shares:
break
- updates.append((share_hash, height))
+ updates.append((share_hash, height, work))
if share_hash in self.heights:
- height_inc, share_hash = self.heights[share_hash]
+ height_inc, share_hash, work_inc = self.heights[share_hash]
else:
- height_inc, share_hash = 1, self.shares[share_hash].previous_hash
+ height_inc, share_hash, work_inc = 1, self.shares[share_hash].previous_hash, target_to_average_attempts(self.shares[share_hash].target)
height += height_inc
- for update_hash, height_then in updates:
- self.heights[update_hash] = height - height_then, share_hash
- #assert (height, share_hash) == self.get_height_and_last2(orig), ((height, share_hash), self.get_height_and_last2(orig))
- return height, share_hash
-
- def get_height_and_last2(self, share_hash):
- assert isinstance(share_hash, (int, long, type(None)))
- height = 0
- while True:
- if share_hash not in self.shares:
- break
- share_hash = self.shares[share_hash].previous_hash
- height += 1
- return height, share_hash
+ work += work_inc
+ for update_hash, height_then, work_then in updates:
+ self.heights[update_hash] = height - height_then, share_hash, work - work_then
+ return height, work, share_hash
def get_chain_known(self, start_hash):
assert isinstance(start_hash, (int, long, type(None)))
def get_highest_height(self):
return max(self.get_height_and_last(head)[0] for head in self.heads) if self.heads else 0
-
- def get_nth_parent_hash(self, item_hash, n):
- if n < 0:
- raise ValueError('n must be >= 0')
-
- updates = {}
- while n:
- if item_hash not in self.skips:
- self.skips[item_hash] = math.geometric(.5), [(1, self.shares[item_hash].previous_hash)]
- skip_length, skip = self.skips[item_hash]
-
- for i in xrange(skip_length):
- if i in updates:
- n_then, that_hash = updates.pop(i)
- x, y = self.skips[that_hash]
- assert len(y) == i
- y.append((n_then - n, item_hash))
-
- for i in xrange(len(skip), skip_length):
- updates[i] = n, item_hash
-
- for i, (dist, then_hash) in enumerate(reversed(skip)):
- if dist <= n:
- break
- else:
- raise AssertionError()
-
- n -= dist
- item_hash = then_hash
-
- return item_hash
-
- def get_nth_parent2(self, item_hash, n):
- x = item_hash
- for i in xrange(n):
- x = self.shares[item_hash].previous_hash
- return x
class FakeShare(object):
def __init__(self, **kwargs):
t = Tracker()
- for i in xrange(100):
- t.add(FakeShare(i, i - 1 if i > 0 else None))
+ for i in xrange(10000):
+ t.add(FakeShare(hash=i, previous_hash=i - 1 if i > 0 else None))
- t.remove(99)
+ #t.remove(99)
print "HEADS", t.heads
print "TAILS", t.tails
import random
- while True:
+ while False:
print
print '-'*30
print
#for share_hash, share in sorted(t.shares.iteritems()):
# print share_hash, share.previous_hash, t.heads.get(share_hash), t.tails.get(share_hash)
- import sys;sys.exit()
+ #import sys;sys.exit()
print t.get_nth_parent_hash(9000, 5000)
print t.get_nth_parent_hash(9001, 412)
def __init__(self, header):
self.hash = bitcoin_data.block_header_type.hash256(header)
self.previous_hash = header['previous_block']
+ self.target = header['target']
class HeightTracker(object):
'''Point this at a factory and let it take care of getting block heights'''
share_data_type = bitcoin_data.ComposedType([
('previous_share_hash', bitcoin_data.PossiblyNone(0, bitcoin_data.HashType())),
- ('target2', bitcoin_data.FloatingIntegerType()),
+ ('target', bitcoin_data.FloatingIntegerType()),
('nonce', bitcoin_data.VarStrType()),
])
class Share(object):
peer = None
+ highest_block_on_arrival = None
gentx = None
@classmethod
raise ValueError('merkle_branch too long!')
self.header = header
+ self.previous_block = header['previous_block']
self.share_info = share_info
self.merkle_branch = merkle_branch
self.other_txs = other_txs
raise ValueError('new_script too long!')
self.previous_hash = self.previous_share_hash = self.share_data['previous_share_hash']
- self.target2 = self.share_data['target2']
+ self.target = self.share_data['target']
self.nonce = self.share_data['nonce']
if len(self.nonce) > 20:
self.bitcoin_hash = bitcoin_data.block_header_type.hash256(header)
self.hash = share1a_type.hash256(self.as_share1a())
- if self.bitcoin_hash > self.target2:
+ if self.bitcoin_hash > self.target:
print 'hash', hex(self.bitcoin_hash)
- print 'targ', hex(self.target2)
+ print 'targ', hex(self.target)
raise ValueError('not enough work!')
if script.get_sigop_count(self.new_script) > 1:
raise ValueError('''block size too large''')
self.gentx = gentx
-
- return Share2(self)
def flag_shared(self):
self.shared = True
def get_pool_attempts_per_second(tracker, previous_share_hash, net):
chain = list(itertools.islice(tracker.get_chain_to_root(previous_share_hash), net.TARGET_LOOKBEHIND))
- attempts = sum(bitcoin_data.target_to_average_attempts(share.target2) for share in chain[:-1])
+ attempts = sum(bitcoin_data.target_to_average_attempts(share.target) for share in chain[:-1])
time = chain[0].timestamp - chain[-1].timestamp
if time == 0:
time = 1
print "start generate_transaction"
height, last = tracker.get_height_and_last(previous_share_hash)
if height < net.TARGET_LOOKBEHIND:
- target2 = bitcoin_data.FloatingIntegerType().truncate_to(2**256//2**32 - 1)
+ target = bitcoin_data.FloatingIntegerType().truncate_to(2**256//2**20 - 1)
else:
attempts_per_second = get_pool_attempts_per_second(tracker, previous_share_hash, net)
pre_target = 2**256//(net.SHARE_PERIOD*attempts_per_second) - 1
previous_share = tracker.shares[previous_share_hash] if previous_share_hash is not None else None
- pre_target2 = math.clip(pre_target, (previous_share.target2*9//10, previous_share.target2*11//10))
+ pre_target2 = math.clip(pre_target, (previous_share.target*9//10, previous_share.target*11//10))
pre_target3 = math.clip(pre_target2, (0, 2**256//2**32 - 1))
- target2 = bitcoin_data.FloatingIntegerType().truncate_to(pre_target3)
+ target = bitcoin_data.FloatingIntegerType().truncate_to(pre_target3)
attempts_to_block = bitcoin_data.target_to_average_attempts(block_target)
max_weight = net.SPREAD * attempts_to_block
class fake_share(object):
pass
fake_share.new_script = new_script
- fake_share.target2 = target2
+ fake_share.target = target
dest_weights = {}
total_weight = 0
for share in itertools.chain([fake_share], itertools.islice(tracker.get_chain_to_root(previous_share_hash), net.CHAIN_LENGTH)):
- weight = bitcoin_data.target_to_average_attempts(share.target2)
+ weight = bitcoin_data.target_to_average_attempts(share.target)
if weight > max_weight - total_weight:
weight = max_weight - total_weight
break
'''
- this_weight = min(bitcoin_data.target_to_average_attempts(target2), max_weight)
+ this_weight = min(bitcoin_data.target_to_average_attempts(target), max_weight)
other_weights, other_weights_total = tracker.get_cumulative_weights(previous_share_hash, min(height, net.CHAIN_LENGTH), max(0, max_weight - this_weight))
dest_weights, total_weight = math.add_dicts([{new_script: this_weight}, other_weights]), this_weight + other_weights_total
total_weight = sum(dest_weights.itervalues())
share_data=dict(
previous_share_hash=previous_share_hash,
nonce=nonce,
- target2=target2,
+ target=target,
),
)),
)],
self.verified.add(share)
return True
- def think(self, ht, now):
+ def think(self, ht, previous_block, now):
if __debug__:
print "start think"
if head_height < self.net.CHAIN_LENGTH and last_last_hash is not None:
desired.add((self.verified.shares[random.choice(list(self.verified.reverse_shares[last_hash]))].peer, last_last_hash))
+ # decide best tree
+ best_tail = max(self.verified.tails, key=lambda h: self.score(max(self.verified.tails[h], key=self.verified.get_height), ht)) if self.verified.tails else None
# decide best verified head
- scores = sorted(self.verified.heads, key=lambda h: self.score(h, ht))
+ scores = sorted(self.verified.tails.get(best_tail, []), key=lambda h: (self.verified.get_work(h), ht.get_min_height(self.verified.shares[h].previous_block)))
- if __debug__:
- for a in scores:
- print ' ', '%x'%a, self.score(a, ht)
+
+ #tif __debug__:
+ #print len(self.verified.tails.get(best_tail, []))
+ #for a in scores:
+ # print ' ', '%x'%a, self.score(a, ht)
for share_hash in scores[:-5]:
if self.shares[share_hash].time_seen > time.time() - 30:
best = scores[-1] if scores else None
+ if best is not None:
+ best_share = self.verified.shares[best]
+ if ht.get_min_height(best_share.header['previous_block']) < ht.get_min_height(best_share.highest_block_on_arrival):
+ if __debug__:
+ print "stale detected!"
+ best = best_share.previous_hash
+
if __debug__:
print "end think"
# XXX this must go in the opposite direction for max_height to make sense
for share in reversed(list(itertools.islice(self.verified.get_chain_known(share2_hash), self.net.CHAIN_LENGTH))):
max_height = max(max_height, ht.get_min_height(share.header['previous_block']))
- attempts += bitcoin_data.target_to_average_attempts(share.target2)
+ attempts += bitcoin_data.target_to_average_attempts(share.target)
this_score = attempts//(ht.get_highest_height() - max_height + 1)
- #this_score = -(ht.get_highest_height() - max_height + 1)//attempts
if this_score > score2:
score2 = this_score
- res = (min(head_height, self.net.CHAIN_LENGTH), score2, -self.verified.shares[share_hash].time_seen)
- #print res
- return res
+ return min(head_height, self.net.CHAIN_LENGTH), score2
class Mainnet(bitcoin_data.Mainnet):
@defer.inlineCallbacks
def set_real_work():
work, height = yield getwork(bitcoind)
- current_work2.set(dict(
- time=work.timestamp,
- ))
- best, desired = tracker.think(ht, current_work2.value['time'])
+ best, desired = tracker.think(ht, work.previous_block, work.timestamp)
for peer2, share_hash in desired:
if peer2 is None:
continue
height=height,
best_share_hash=best,
))
+ current_work2.set(dict(
+ time=work.timestamp,
+ ))
print 'Initializing work...'
yield set_real_work()
#print 'Received share %x' % (share.hash,)
tracker.add(share)
- best, desired = tracker.think(ht, current_work2.value['time'])
+ best, desired = tracker.think(ht, current_work.value['previous_block'], current_work2.value['time'])
#for peer2, share_hash in desired:
# print 'Requesting parent share %x' % (share_hash,)
# peer2.send_getshares(hashes=[share_hash], parents=2000)
net=args.net,
)
print 'Generating!'
- #print 'Target: %x' % (p2pool.coinbase_type.unpack(generate_tx['tx_ins'][0]['script'])['share_data']['target2'],)
+ #print 'Target: %x' % (p2pool.coinbase_type.unpack(generate_tx['tx_ins'][0]['script'])['share_data']['target'],)
#, have', shares.count(my_script) - 2, 'share(s) in the current chain. Fee:', sum(tx.value_in - tx.value_out for tx in extra_txs)/100000000
transactions = [generate_tx] + [tx.tx for tx in extra_txs]
merkle_root = bitcoin.data.merkle_hash(transactions)
print 'Toff', timestamp2 - timestamp
timestamp = timestamp2
ba = bitcoin.getwork.BlockAttempt(state['version'], state['previous_block'], merkle_root, timestamp, state['target'])
- #print 'SENT', 2**256//p2pool.coinbase_type.unpack(generate_tx['tx_ins'][0]['script'])['share_data']['target2']
- target = p2pool.coinbase_type.unpack(generate_tx['tx_ins'][0]['script'])['share_data']['target2']
+ #print 'SENT', 2**256//p2pool.coinbase_type.unpack(generate_tx['tx_ins'][0]['script'])['share_data']['target']
+ target = p2pool.coinbase_type.unpack(generate_tx['tx_ins'][0]['script'])['share_data']['target']
if not all_targets:
target = min(2**256//2**32 - 1, target)
return ba.getwork(target)
return
share = p2pool_data.Share.from_share1a(share1a)
share.peer = self # XXX
+ share.highest_block_on_arrival = self.node.current_work.value['previous_block'] # XXX
self.node.handle_share(share, self)
message_share1bs = bitcoin_data.ComposedType([
return
share = p2pool_data.Share.from_share1b(share1b)
share.peer = self # XXX
+ share.highest_block_on_arrival = self.node.current_work.value['previous_block'] # XXX
self.node.handle_share(share, self)
def send_shares(self, shares, full=False):
if element is None:
return (2**256, {}, 0) # XXX
share = self.tracker.shares[element]
- att = bitcoin_data.target_to_average_attempts(share.target2)
+ att = bitcoin_data.target_to_average_attempts(share.target)
return 1, {share.new_script: att}, att
def combine_deltas(self, (share_count1, weights1, total_weight1), (share_count2, weights2, total_weight2)):
t = data.Tracker()
d = WeightsSkipList(t)
for i in xrange(2000):
- t.add(data.FakeShare(hash=i, previous_hash=i - 1 if i > 0 else None, new_script=i, target2=random.randrange(2**249, 2**250)))
+ t.add(data.FakeShare(hash=i, previous_hash=i - 1 if i > 0 else None, new_script=i, target=random.randrange(2**249, 2**250)))
for i in xrange(2000):
#a = random.randrange(2000)
a = 1999