def fill_from_rpc(self, data): '''Convert getblocktemplate result into BlockTemplate instance''' commitment = None nTime = data['curtime'] if data.has_key('curtime') else None if settings.COINDAEMON_HAS_SEGWIT: txids = [] hashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] try: txids = [None] + [ util.ser_uint256(int(t['txid'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txids) except KeyError: mt = merkletree.MerkleTree(hashes) wmt = merkletree.MerkleTree(hashes).withFirst(binascii.unhexlify('0000000000000000000000000000000000000000000000000000000000000000')) self.witness = SHA256.new(SHA256.new(wmt + witness_nonce).digest()).digest() commitment = b'\x6a' + struct.pack(">b", len(self.witness) + len(witness_magic)) + witness_magic + self.witness try: default_witness = data['default_witness_commitment'] commitment_check = binascii.unhexlify(default_witness) if(commitment != commitment_check): print("calculated witness does not match supplied one! This block probably will not be accepted!") commitment = commitment_check except KeyError: pass self.witness = commitment[6:] else: txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txhashes) coinbase = CoinbaseTransaction(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], commitment, settings.COINBASE_EXTRAS, nTime) self.height = data['height'] self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) self.nBits = int(data['bits'], 16) self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 self.vtx = [ coinbase, ] for tx in data['transactions']: t = TxBlob() t.deserialize(binascii.unhexlify(tx['data'])) self.vtx.append(t) self.curtime = data['curtime'] self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) # Reversed prevhash self.prevhash_bin = binascii.unhexlify(util.reverse_hash(data['previousblockhash'])) self.prevhash_hex = "%064x" % self.hashPrevBlock self.broadcast_args = self.build_broadcast_args()
def serialize_header(self, merkle_root_int, ntime_bin, nonce_bin): '''Serialize header for calculating block hash''' r = struct.pack("<i", self.nVersion) r += util.ser_uint256(self.hashPrevBlock) r += util.ser_uint256(merkle_root_int) r += ntime_bin[::-1] r += struct.pack("<I", self.nBits) r += nonce_bin[::-1] return r
def serialize(self): r = [] r.append(struct.pack("<i", self.nVersion)) r.append(util.ser_uint256(self.hashPrevBlock)) r.append(util.ser_uint256(self.hashMerkleRoot)) r.append(struct.pack("<I", self.nTime)) r.append(struct.pack("<I", self.nBits)) r.append(struct.pack("<I", self.nNonce)) r.append(util.ser_vector(self.vtx)) return ''.join(r)
def serialize(self): r = [ struct.pack("<i", self.nVersion), util.ser_uint256(self.hashPrevBlock), util.ser_uint256(self.hashMerkleRoot), struct.pack("<I", self.nTime), struct.pack("<I", self.nBits), struct.pack("<I", self.nNonce), util.ser_vector(self.vtx) ] return ''.join(r)
def fill_from_rpc(self, data): '''Convert getblocktemplate result into BlockTemplate instance''' #txhashes = [None] + [ binascii.unhexlify(t['hash']) for t in data['transactions'] ] txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txhashes) coinbase = self.coinbase_transaction_class(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], settings.COINBASE_EXTRAS) self.height = data['height'] self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) self.nBits = int(data['bits'], 16) self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 self.vtx = [ coinbase, ] for tx in data['transactions']: t = halfnode.CTransaction() t.deserialize(StringIO.StringIO(binascii.unhexlify(tx['data']))) self.vtx.append(t) self.curtime = data['curtime'] self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) # Reversed prevhash self.prevhash_bin = binascii.unhexlify(util.reverse_hash(data['previousblockhash'])) self.prevhash_hex = "%064x" % self.hashPrevBlock self.broadcast_args = self.build_broadcast_args()
def serialize(self): r = [] r.append(struct.pack("<i", self.nVersion)) r.append(util.ser_uint256(self.hashPrevBlock)) r.append(util.ser_uint256(self.hashMerkleRoot)) r.append(struct.pack("<I", self.nTime)) r.append(struct.pack("<I", self.nBits)) r.append(struct.pack("<I", self.nNonce)) r.append(util.ser_uint256(self.hashstateroot)) r.append(util.ser_uint256(self.hashutxoroot)) r.append( binascii.unhexlify( "0000000000000000000000000000000000000000000000000000000000000000ffffffff00" )) r.append(util.ser_vector(self.vtx)) return ''.join(r)
def fill_from_rpc(self, data): '''Convert getblocktemplate result into BlockTemplate instance''' payee = None if (data['payee'] != ''): payee = util.script_to_address(data['payee']) payee_amount = data.get('payee_amount', data['coinbasevalue'] / 5) #default to 20% #txhashes = [None] + [ binascii.unhexlify(t['hash']) for t in data['transactions'] ] txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txhashes) coinbase = CoinbaseTransactionPOW(self.timestamper, self.coinbaser, payee, payee_amount, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], settings.COINBASE_EXTRAS) self.height = data['height'] self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) self.nBits = int(data['bits'], 16) self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 self.masternode_payments = data['masternode_payments'] self.vtx = [ coinbase, ] for vote in data['votes']: v = halfnode.CMasterNodeVote() v.deserialize(StringIO.StringIO(binascii.unhexlify(vote))) self.vmn.append(v) for tx in data['transactions']: t = halfnode.CTransaction() t.deserialize(StringIO.StringIO(binascii.unhexlify(tx['data']))) self.vtx.append(t) self.curtime = data['curtime'] self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) # Reversed prevhash self.prevhash_bin = binascii.unhexlify( util.reverse_hash(data['previousblockhash'])) self.prevhash_hex = "%064x" % self.hashPrevBlock self.broadcast_args = self.build_broadcast_args()
def __init__(self, root): if type(root) == long: root = util.ser_uint256(root) self.valid = [root, [], []] self.state = TreeState() # A dict for purgatory is a bit of a hack, and should probably be fixed before switching to C++. self.purgatory = {} # key = (level, index); value = [candidate hashes] self.peerorigins = {} # key = hash, value = peer self.txcounthints = [] # we can't trust it when a peer says how many txes there are, so they're just hints self.levels = 0 self.txcount = 0
def fill_from_rpc(self, data, aux_data): '''Convert getblocktemplate result into BlockTemplate instance''' self.auxs = aux_data self.tree, self.merkle_size = util.make_auxpow_tree(aux_data) self.aux_targets = [None for i in self.auxs] merkle_leaves = [ ('0' * 64) for x in range(self.merkle_size) ] for chain in range(len(self.auxs)): merkle_index = self.tree[self.auxs[chain]['chainid']] merkle_leaves[merkle_index] = self.auxs[chain]['hash'] target = self.auxs[chain]['target'].decode('hex')[::-1].encode('hex') self.aux_targets[chain] = int(target, 16) log.info("Merged Chain: %i network difficulty: %s" % (self.auxs[chain]['chainid'], float(util.diff_to_target(self.aux_targets[chain])))) self.merkle_hashes = [ int(t, 16) for t in merkle_leaves ] self.mm_data = '\xfa\xbemm' + util.aux_pow_coinbase_type.pack(dict( merkle_root = util.merkle_hash(self.merkle_hashes), size = self.merkle_size, nonce = 0, )) txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txhashes) coinbase = CoinbaseTransaction(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], settings.COINBASE_EXTRAS + self.mm_data, data['curtime']) self.height = data['height'] self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) self.nBits = int(data['bits'], 16) self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 self.vtx = [ coinbase, ] for tx in data['transactions']: t = halfnode.CTransaction() t.deserialize(StringIO.StringIO(binascii.unhexlify(tx['data']))) self.vtx.append(t) self.curtime = data['curtime'] self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) log.info("MainNet Block Height: %i network difficulty: %s" % (self.height, float(util.diff_to_target(self.target)))) # Reversed prevhash self.prevhash_bin = binascii.unhexlify(util.reverse_hash(data['previousblockhash'])) self.prevhash_hex = "%064x" % self.hashPrevBlock self.broadcast_args = self.build_broadcast_args()
def __init__(self, root): if type(root) == long: root = util.ser_uint256(root) self.valid = [root, [], []] self.state = TreeState() # A dict for purgatory is a bit of a hack, and should probably be fixed before switching to C++. self.purgatory = {} # key = (level, index); value = [candidate hashes] self.peerorigins = {} # key = hash, value = peer self.levels = 0 self.txcount = 0 self.runs = 0 # number of runs added; used to help decide when to send a new state to peers self.rwlock = rwlock.ReadWriteLock()
def __init__(self, root): if type(root) == long: root = util.ser_uint256(root) self.valid = [root, [], []] self.state = TreeState() # A dict for purgatory is a bit of a hack, and should probably be fixed before switching to C++. self.purgatory = {} # key = (level, index); value = [candidate hashes] self.peerorigins = {} # key = hash, value = peer self.txcounthints = [ ] # we can't trust it when a peer says how many txes there are, so they're just hints self.levels = 0 self.txcount = 0
def fill_from_rpc(self, data): """Convert getblocktemplate result into BlockTemplate instance""" # txhashes = [None] + [ binascii.unhexlify(t['hash']) for t in data['transactions'] ] txhashes = [None] + [util.ser_uint256(int(t["hash"], 16)) for t in data["transactions"]] mt = merkletree.MerkleTree(txhashes) if settings.MAIN_COIN_TYPE == "proof-of-work": coinbase = self.coinbase_transaction_class( self.timestamper, self.coinbaser, data["coinbasevalue"], data["coinbaseaux"]["flags"], data["height"], settings.COINBASE_EXTRAS, ) else: coinbase = self.coinbase_transaction_class( self.timestamper, self.coinbaser, data["coinbasevalue"], data["coinbaseaux"]["flags"], data["height"], settings.COINBASE_EXTRAS, data["curtime"], ) self.height = data["height"] self.nVersion = data["version"] self.hashPrevBlock = int(data["previousblockhash"], 16) self.nBits = int(data["bits"], 16) self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 self.vtx = [coinbase] for tx in data["transactions"]: t = halfnode.CTransaction() t.deserialize(StringIO.StringIO(binascii.unhexlify(tx["data"]))) self.vtx.append(t) self.curtime = data["curtime"] self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) # Reversed prevhash self.prevhash_bin = binascii.unhexlify(util.reverse_hash(data["previousblockhash"])) self.prevhash_hex = "%064x" % self.hashPrevBlock self.broadcast_args = self.build_broadcast_args()
def fill_from_rpc(self, data): '''Convert getblocktemplate result into BlockTemplate instance''' payee = None if(data['payee'] != ''): payee = util.script_to_address(data['payee']) payee_amount = data.get('payee_amount', data['coinbasevalue']/5) #default to 20% #txhashes = [None] + [ binascii.unhexlify(t['hash']) for t in data['transactions'] ] txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txhashes) coinbase = CoinbaseTransactionPOW(self.timestamper, self.coinbaser, payee, payee_amount, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], settings.COINBASE_EXTRAS) self.height = data['height'] self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) self.nBits = int(data['bits'], 16) self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 self.masternode_payments = data['masternode_payments'] self.vtx = [ coinbase, ] for vote in data['votes']: v = halfnode.CMasterNodeVote() v.deserialize(StringIO.StringIO(binascii.unhexlify(vote))) self.vmn.append(v) for tx in data['transactions']: t = halfnode.CTransaction() t.deserialize(StringIO.StringIO(binascii.unhexlify(tx['data']))) self.vtx.append(t) self.curtime = data['curtime'] self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) # Reversed prevhash self.prevhash_bin = binascii.unhexlify(util.reverse_hash(data['previousblockhash'])) self.prevhash_hex = "%064x" % self.hashPrevBlock self.broadcast_args = self.build_broadcast_args()
def calcparent(self, hash1, hash2): if type(hash1) == long: hash1 = util.ser_uint256(hash1) if type(hash2) == long: hash2 = util.ser_uint256(hash2) return util.doublesha(hash1 + hash2)
def addhash(self, level, index, hash, peer=None, locked=False): """ Adds a hash to either the validated tree (when possible) or to the unvalidated cache, self.purgatory. This will also add any computed parent hashes recursively. If the hash makes it into the validated tree, this will also check the nephews of this hash to see if they can now be validated. However, direct descendants will not be checked, and must be checked by the caller. """ if type(hash) == long: hash = util.ser_uint256(hash) key = (level, index) if not locked: self.rwlock.acquire_write( ) # this might benefit from more fine-grained locks when the switch is made to C++ if key in self.purgatory: if self.purgatory[key] == hash: if not locked: self.rwlock.release_write() return else: oldpeer = self.peerorigins[self.purgatory[ key]] if self.purgatory[key] in self.peerorigins else None debuglog( 'btnet', 'Warning: received two different hashes for the same part of a tree. Replacing old hash.' ) debuglog( 'btnet', 'Cause is likely either network corruption or a malicious peer. Peers:' ) debuglog('btnet', oldpeer, peer) debuglog( 'btnet', 'Hash added is (%i, %i): %s. Oldhash: %s.' % (level, index, to_hex(hash), to_hex(self.purgatory[key]))) # fixme: peer banning # continue to add the new hash and validate elif self.getnode(level, index, locked=True): debuglog( 'bttree', 'Debug warning: level=%i index=%i already validated in tree' % (level, index)) if not locked: self.rwlock.release_write() return self.purgatory[key] = hash #self.peerorigins[hash] = peer # fixme: make sure memory growth is bounded parent = self.getnode(level - 1, index // 2, locked=True) # is our parent already valid? siblingkey = (level, index ^ 1) if not siblingkey in self.purgatory: # Is this is the right edge of the tree? if not index & 1: # if even (left sibling) if self.txcount: height = int(math.ceil(math.log(self.txcount, 2))) assert level <= height edge = (self.txcount - 1) >> (height - level) if index == edge: self.purgatory[ siblingkey] = hash # this can be overwritten later if siblingkey in self.purgatory: # then we can check one level up sib = self.purgatory[siblingkey] parenthash = self.calcparent(sib, hash) if ( index % 2) else self.calcparent(hash, sib) # left sibling goes first if parent and parent == parenthash: result = 'connected' elif parent and parent != parenthash and not sib == hash: debuglog( 'btnet', 'Invalid hash(es) encountered when checking (%i, %i): %s.' % (level, index, to_hex(hash))) debuglog( 'btnet', 'Parent (%i, %i) = %s not %s' % (level - 1, index // 2, to_hex(parent), to_hex(parenthash))) result = 'invalid' elif parent and parent != parenthash and sib == hash: debuglog( 'btnet', 'Found a bad edge: (%i, %i) = %s not %s' % (level - 1, index // 2, to_hex(parent), to_hex(parenthash))) result = 'orphan' # incorrect tx count hint else: # recurse one level up result = self.addhash(level - 1, index // 2, parenthash, None, locked=True) else: result = 'orphan' if result == 'connected': self.setnode(level, index, hash, edge=(hash == sib), locked=True) self.setnode(level, index ^ 1, sib, edge=(hash == sib), locked=True) del self.purgatory[key] del self.purgatory[siblingkey] if hash == sib and level == self.levels: # right edge, bottom row self.txcount = index | 1 # left sib is the last tx, but we start counting from 0, so we want the right sib's index # the recursive caller of addhash will take care of the children of key, but not siblingkey if hash != sib: self.checkchildren(siblingkey[0], siblingkey[1], locked=True) elif result == 'invalid': if sib == hash: # invalid hint about the number of transactions debuglog('btnet', 'Invalid txcount? -- %i ' % self.txcount) del self.purgatory[max(siblingkey, key)] result = 'orphan' else: for k in key, siblingkey: # fixme: for multi-level recursion, there's a good chance we're deleting the wrong txes. # should we delete all of the descendants of the lowest valid hash to which this resolves? # or should we leave these hashes all in purgatory? or what? who do we ban? debuglog( 'btnet', 'Invalid hash(es) encountered. Deleting: (%i, %i): %s.' % (k[0], k[1], to_hex(self.purgatory[k]))) #del self.purgatory[k] elif result == 'orphan': pass # fixme: deal with peer info (and banning) in each of these branches above if not locked: self.rwlock.release_write() return result
def fill_from_rpc(self, data, aux_data): '''Convert getblocktemplate result into BlockTemplate instance''' self.auxs = aux_data self.tree, self.merkle_size = util.make_auxpow_tree(aux_data) self.aux_targets = [None for i in self.auxs] merkle_leaves = [('0' * 64) for x in range(self.merkle_size)] for chain in range(len(self.auxs)): merkle_index = self.tree[self.auxs[chain]['chainid']] merkle_leaves[merkle_index] = self.auxs[chain]['hash'] target = self.auxs[chain]['target'].decode('hex')[::-1].encode( 'hex') self.aux_targets[chain] = int(target, 16) log.info("Merged Chain: %i network difficulty: %s" % (self.auxs[chain]['chainid'], float(util.diff_to_target(self.aux_targets[chain])))) self.merkle_hashes = [int(t, 16) for t in merkle_leaves] self.mm_data = '\xfa\xbemm' + util.aux_pow_coinbase_type.pack( dict( merkle_root=util.merkle_hash(self.merkle_hashes), size=self.merkle_size, nonce=0, )) txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txhashes) coinbase = CoinbaseTransaction(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], settings.COINBASE_EXTRAS + self.mm_data, data['curtime']) self.height = data['height'] self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) self.nBits = int(data['bits'], 16) self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 self.vtx = [ coinbase, ] for tx in data['transactions']: t = halfnode.CTransaction() t.deserialize(StringIO.StringIO(binascii.unhexlify(tx['data']))) self.vtx.append(t) self.curtime = data['curtime'] self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) log.info("MainNet Block Height: %i network difficulty: %s" % (self.height, float(util.diff_to_target(self.target)))) # Reversed prevhash self.prevhash_bin = binascii.unhexlify( util.reverse_hash(data['previousblockhash'])) self.prevhash_hex = "%064x" % self.hashPrevBlock self.broadcast_args = self.build_broadcast_args()
def fill_from_rpc(self, data): '''Convert getblocktemplate result into BlockTemplate instance''' commitment = None nTime = data['curtime'] if data.has_key('curtime') else None if settings.COINDAEMON_HAS_SEGWIT: txids = [] hashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] try: txids = [None] + [ util.ser_uint256(int(t['txid'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txids) except KeyError: mt = merkletree.MerkleTree(hashes) wmt = merkletree.MerkleTree(hashes).withFirst( binascii.unhexlify( '0000000000000000000000000000000000000000000000000000000000000000' )) self.witness = SHA256.new( SHA256.new(wmt + witness_nonce).digest()).digest() commitment = b'\x6a' + struct.pack( ">b", len(self.witness) + len(witness_magic)) + witness_magic + self.witness try: default_witness = data['default_witness_commitment'] commitment_check = binascii.unhexlify(default_witness) if (commitment != commitment_check): print( "calculated witness does not match supplied one! This block probably will not be accepted!" ) commitment = commitment_check except KeyError: pass self.witness = commitment[6:] else: txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ] mt = merkletree.MerkleTree(txhashes) coinbase = CoinbaseTransaction(self.timestamper, self.coinbaser, data['coinbasevalue'], data['coinbaseaux']['flags'], data['height'], commitment, settings.COINBASE_EXTRAS, nTime) self.height = data['height'] self.nVersion = data['version'] self.hashPrevBlock = int(data['previousblockhash'], 16) self.nBits = int(data['bits'], 16) self.hashMerkleRoot = 0 self.nTime = 0 self.nNonce = 0 self.vtx = [ coinbase, ] for tx in data['transactions']: t = TxBlob() t.deserialize(binascii.unhexlify(tx['data'])) self.vtx.append(t) self.curtime = data['curtime'] self.timedelta = self.curtime - int(self.timestamper.time()) self.merkletree = mt self.target = util.uint256_from_compact(self.nBits) # Reversed prevhash self.prevhash_bin = binascii.unhexlify( util.reverse_hash(data['previousblockhash'])) self.prevhash_hex = "%064x" % self.hashPrevBlock self.broadcast_args = self.build_broadcast_args()
def addhash(self, level, index, hash, peer=None): """ Adds a hash to either the validated tree (when possible) or to the unvalidated cache, self.purgatory. This will also add any computed parent hashes recursively. If the hash makes it into the validated tree, this will also check the nephews of this hash to see if they can now be validated. However, direct descendents will not be checked, and must be checked by the caller. """ if type(hash) == long: hash = util.ser_uint256(hash) key = (level, index) if key in self.purgatory: if self.purgatory[key] == hash: return else: oldpeer = self.peerorigins[self.purgatory[key]] if self.purgatory[key] in self.peerorigins else None debuglog('btnet', 'Warning: received two different hashes for the same part of a tree. Replacing old hash.') debuglog('btnet', 'Cause is likely either network corruption or a malicious peer. Peers:') debuglog('btnet', oldpeer, peer) debuglog('btnet', 'Hash added is (%i, %i): %s. Oldhash: %s.' % (level, index, to_hex(hash), to_hex(self.purgatory[key]))) # fixme: peer banning # continue to add the new hash and validate elif self.getnode(level, index): debuglog('bttree', 'Debug warning: level=%i index=%i already validated in tree' % (level, index)) return self.purgatory[key] = hash #self.peerorigins[hash] = peer # fixme: make sure memory growth is bounded parent = self.getnode(level-1, index//2) # is our parent already valid? #if parent: print "valid parent of %i,%i is %i,%i:" %(level, index, level-1, index//2), to_hex(parent]) siblingkey = (level, index ^ 1) if not siblingkey in self.purgatory: # Is this is the right edge of the tree? if not index & 1: # if even (left sibling) for hint in self.txcounthints: height = int(math.ceil(math.log(hint, 2))) if level > height: continue edge = (hint-1) >> (height - level) if index == edge: self.purgatory[siblingkey] = hash # this can be overwritten later break if siblingkey in self.purgatory: # then we can check one level up sib = self.purgatory[siblingkey] parenthash = self.calcparent(sib, hash) if (index%2) else self.calcparent(hash, sib) # left sibling goes first if parent and parent == parenthash: result = 'connected' elif parent and parent != parenthash and not sib == hash: debuglog('btnet', 'Invalid hash(es) encountered when checking (%i, %i): %s.' % (level, index, to_hex(hash))) debuglog('btnet', 'Parent (%i, %i) = %s not %s' % (level-1, index//2, to_hex(parent), to_hex(parenthash))) result = 'invalid' elif parent and parent != parenthash and sib == hash: debuglog('btnet', 'Found a bad edge: (%i, %i) = %s not %s' % (level-1, index//2, to_hex(parent), to_hex(parenthash))) result = 'orphan' # incorrect tx count hint else: # recurse one level up result = self.addhash(level-1, index//2, parenthash, None) else: result = 'orphan' if result == 'connected': self.setnode(level, index, hash, edge=(hash==sib)) self.setnode(level, index^1, sib, edge=(hash==sib)) del self.purgatory[key] del self.purgatory[siblingkey] if hash == sib and level == self.levels: # right edge, bottom row self.txcount = index|1-1 # left sib's index # the recursive caller of addhash will take care of the children of key, but not siblingkey self.checkchildren(siblingkey[0], siblingkey[1]) elif result == 'invalid': if sib == hash: # invalid hint about the number of transactions debuglog('btnet', 'Invalid txcount hint: %i among ' % hint, self.txcounthints) del self.purgatory[max(siblingkey, key)] result = 'orphan' else: for k in key, siblingkey: # fixme: for multi-level recursion, there's a good chance we're deleting the wrong txes. # should we delete all of the decendants of the lowest valid hash to which this resolves? # or should we leave these hashes all in purgatory? or what? who do we ban? debuglog('btnet', 'Invalid hash(es) encountered. Deleting: (%i, %i): %s.' % (k[0], k[1], to_hex(self.purgatory[k]))) #del self.purgatory[k] elif result == 'orphan': pass # fixme: deal with peer info (and banning) in each of these branches above return result