class WalletVerifier(threading.Thread):
+ """ Simple Payment Verification """
def __init__(self, interface, config):
threading.Thread.__init__(self)
self.set_local_height()
def get_confirmations(self, tx):
- return (self.local_height - self.verified_tx[tx] + 1) if tx in self.verified_tx else 0
+ """ return the number of confirmations of a monitored transaction. """
+ with self.lock:
+ assert tx in self.transactions
+ return (self.local_height - self.verified_tx[tx] + 1) if tx in self.verified_tx else 0
def add(self, tx):
+ """ add a transaction to the list of monitored transactions. """
with self.lock:
if tx not in self.transactions:
self.transactions.append(tx)
requested_merkle = []
requested_chunks = []
requested_headers = []
- pending_headers_changed = False
+ all_chunks = False
# subscribe to block headers
self.interface.send([ ('blockchain.headers.subscribe',[])], 'verifier')
while True:
# request missing chunks
max_index = (self.height+1)/2016
- if not requested_chunks:
+ if not all_chunks and self.height and not requested_chunks:
for i in range(0, max_index + 1):
# test if we can read the first header of the chunk
if self.read_header(i*2016): continue
- print "requesting chunk", i
+ # print "requesting chunk", i
self.interface.send([ ('blockchain.block.get_chunk',[i])], 'verifier')
requested_chunks.append(i)
break
+ else:
+ all_chunks = True
+ print "all chunks"
- # request missing headers
- if not requested_chunks and self.local_height:
- for i in range(self.local_height + 1, self.height + 1):
- if i not in requested_headers:
- print "requesting header", i
- self.interface.send([ ('blockchain.block.get_header',[i])], 'verifier')
- requested_headers.append(i)
-
# request missing tx merkle
for tx in self.transactions:
if tx not in self.verified_tx:
self.request_merkle(tx)
#break
+
+ # process pending headers
+ if self.pending_headers and all_chunks:
+ done = []
+ for header in self.pending_headers:
+ if self.verify_header(header):
+ done.append(header)
+ else:
+ # request previous header
+ i = header.get('block_height') - 1
+ if i not in requested_headers:
+ print "requesting header", i
+ self.interface.send([ ('blockchain.block.get_header',[i])], 'verifier')
+ requested_headers.append(i)
+ # no point continuing
+ break
+ for header in done: self.pending_headers.remove(header)
+ self.interface.trigger_callback('updated')
+
try:
r = self.interface.get_response('verifier',timeout=1)
except Queue.Empty:
self.verify_chunk(index, result)
requested_chunks.remove(index)
- elif method == 'blockchain.headers.subscribe':
- self.height = result.get('block_height')
- self.pending_headers.append(result)
- pending_headers_changed = True
+ elif method in ['blockchain.headers.subscribe', 'blockchain.block.get_header']:
- elif method == 'blockchain.block.get_header':
- height = result.get('block_height')
- requested_headers.remove(height)
self.pending_headers.append(result)
- pending_headers_changed = True
-
- # process pending headers
- if pending_headers_changed:
+ if method == 'blockchain.block.get_header':
+ requested_headers.remove(result.get('block_height'))
+ else:
+ self.height = result.get('block_height')
+
self.pending_headers.sort(key=lambda x: x.get('block_height'))
- print "pending headers", map(lambda x: x.get('block_height'), self.pending_headers)
- for header in self.pending_headers:
- if self.verify_header(header):
- self.pending_headers.remove(header)
- else:
- break
- pending_headers_changed = False
+ # print "pending headers", map(lambda x: x.get('block_height'), self.pending_headers)
+
+
self.interface.trigger_callback('updated')
def verify_merkle(self, tx_hash, result):
tx_height = result.get('block_height')
- self.merkle_roots[tx_hash] = self.hash_merkle_root(result['merkle'], tx_hash)
+ self.merkle_roots[tx_hash] = self.hash_merkle_root(result['merkle'], tx_hash, result.get('pos'))
header = self.read_header(tx_height)
if header:
assert header.get('merkle_root') == self.merkle_roots[tx_hash]
data = hexdata.decode('hex')
height = index*2016
num = len(data)/80
- print "validate_chunk", index, num
+ print "validating headers", height, num
if index == 0:
previous_hash = ("0"*64)
prev_header = self.read_header(height -1)
if not prev_header:
print "no previous header", height
- return
+ return False
#prev_hash = prev_header.get('block_height')
prev_hash = self.hash_header(prev_header)
assert prev_hash == header.get('prev_block_hash')
assert bits == header.get('bits')
assert eval('0x'+_hash) < target
- ok = True
except:
print "verify header failed", header
- raise
- # this could be caused by a reorg. request the previous header
- ok = False
- #request previous one
-
- if ok:
- self.save_header(header)
- print "verify header: ok", height
- return True
+ # this can be caused by a reorg. returning False will request the previous header.
+ return False
+
+ self.save_header(header)
+ print "verify header: ok", height
+ return True
h['nonce'] = hex_to_int(s[76:80])
return h
-
def hash_header(self, header):
return rev_hex(Hash(self.header_to_string(header).decode('hex')).encode('hex'))
-
- def hash_merkle_root(self, merkle_s, target_hash):
+ def hash_merkle_root(self, merkle_s, target_hash, pos):
h = hash_decode(target_hash)
- for item in merkle_s:
- is_left = item[0] == 'L'
- h = Hash( h + hash_decode(item[1:]) ) if is_left else Hash( hash_decode(item[1:]) + h )
+ for i in range(len(merkle_s)):
+ item = merkle_s[i]
+ h = Hash( hash_decode(item) + h ) if ((pos >> i) & 1) else Hash( h + hash_decode(item) )
return hash_encode(h)
def path(self):
self.set_local_height()
def save_header(self, header):
+ # todo: invalidate tx verifications if we rewind
data = self.header_to_string(header).decode('hex')
assert len(data) == 80
height = header.get('block_height')