def main(args): btc = BitcoinClient() tmp_address = btc.validate_address(btc.get_new_address()) print "fetching charter: %s" % CHARTER_URL charter = fetch_charter(CHARTER_URL) client_pubkey = tmp_address['pubkey'] oracle_pubkeys = [] for o in charter['nodes']: # print json.dumps(o) oracle_pubkeys.append(o['pubkey']) min_sigs = int(ceil(float(len(oracle_pubkeys)) / 2)) print "number of nodes: %i" % len(charter['nodes']) print "required signatures: %i" % min_sigs sum_fees_satoshi = 0 for o in charter['nodes']: sum_fees_satoshi += Decimal(o['fee']) * 100000000 sum_fees_satoshi += Decimal(charter['org_fee']) * 100000000 key_list = [client_pubkey] + oracle_pubkeys response = btc.create_multisig_address(min_sigs, key_list) print "" print "1. wire the funds to %s" % response['address'] print " oracle & org fees: %i satoshi (as detailed in %s)" % ( sum_fees_satoshi, CHARTER_URL) print " miners fee: %i satoshi (see MINERS_FEE in src/client/main.py if you want to lower it)" % MINERS_FEE print "2. wait for transaction to get any confirmations" print "3. run:" print "%s main2 %s <locktime_minutes> <return_address>" % (START_COMMAND, client_pubkey)
def main(args): btc = BitcoinClient() tmp_address = btc.validate_address(btc.get_new_address()) print "fetching charter: %s" % CHARTER_URL charter = fetch_charter(CHARTER_URL) client_pubkey = tmp_address['pubkey'] oracle_pubkeys = [] for o in charter['nodes']: # print json.dumps(o) oracle_pubkeys.append(o['pubkey']) min_sigs = int(ceil(float(len(oracle_pubkeys))/2)) print "number of nodes: %i" % len(charter['nodes']) print "required signatures: %i" % min_sigs sum_fees_satoshi = 0 for o in charter['nodes']: sum_fees_satoshi += Decimal(o['fee'])*100000000 sum_fees_satoshi += Decimal(charter['org_fee'])*100000000 key_list = [client_pubkey] + oracle_pubkeys response = btc.create_multisig_address(min_sigs, key_list) print "" print "1. wire the funds to %s" % response['address'] print " oracle & org fees: %i satoshi (as detailed in %s)" % (sum_fees_satoshi , CHARTER_URL) print " miners fee: %i satoshi (see MINERS_FEE in src/client/main.py if you want to lower it)" % MINERS_FEE print "2. wait for transaction to get any confirmations" print "3. run:" print "%s main2 %s <locktime_minutes> <return_address>" % ( START_COMMAND, client_pubkey )
def __init__(self): self.communication = OracleCommunication() self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self)
def tx_info(args): tx = args[0] btc = BitcoinClient() prevtxs = '[{"redeemScript": "52210281cf9fa9241f0a9799f27a4d5d60cff74f30eed1d536bf7a72d3dec936c151632102e8e22190b0adfefd0962c6332e74ab68831d56d0bfc2b01b32beccd56e3ef6f021035ff60e6745093b9bcbae93082e1c50ca5b3fcf8bcd186a46da46ded5132530522103a9bd3bfbd9f9b1719d3ecad8658796dc5e778177d77145b5c37247eb3060861854ae", "txid": "10a3ab54e1e19701fcb86c7725621b5b1b26415f94363de35a493ba9ca502b15", "vout": 0, "scriptPubKey": "a914a37ce66d7065157037e90ca4d4b4a20d8d865a2687"}]' prevtxs = json.loads(prevtxs) pprint.pprint(btc.decode_raw_transaction(tx)) pprint.pprint(btc.signatures_count(tx, prevtxs)) pprint.pprint(btc.signatures(tx, prevtxs))
def tx_info(args): tx = args[0] btc = BitcoinClient() prevtxs = '[{"redeemScript": "52210281cf9fa9241f0a9799f27a4d5d60cff74f30eed1d536bf7a72d3dec936c151632102e8e22190b0adfefd0962c6332e74ab68831d56d0bfc2b01b32beccd56e3ef6f021035ff60e6745093b9bcbae93082e1c50ca5b3fcf8bcd186a46da46ded5132530522103a9bd3bfbd9f9b1719d3ecad8658796dc5e778177d77145b5c37247eb3060861854ae", "txid": "10a3ab54e1e19701fcb86c7725621b5b1b26415f94363de35a493ba9ca502b15", "vout": 0, "scriptPubKey": "a914a37ce66d7065157037e90ca4d4b4a20d8d865a2687"}]' prevtxs = json.loads(prevtxs) pprint.pprint( btc.decode_raw_transaction(tx)) pprint.pprint (btc.signatures_count(tx, prevtxs)) pprint.pprint (btc.signatures(tx, prevtxs))
def __init__(self): self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self) last_received = self.kv.get_by_section_key('fastcast', 'last_epoch') if not last_received: self.kv.store('fastcast', 'last_epoch', {'last': 0}) self.set_fastcast_address()
def __init__(self): self.communication = MockBitmessageCommunication() self.db = MockOracleDb() self.btc = BitcoinClient(account=TEST_ACCOUNT) self.evaluator = Evaluator() self.task_queue = TaskQueue(self.db) self.handlers = defaultdict(lambda: None, handlers)
def __init__(self): self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self) last_received = self.kv.get_by_section_key('fastcast', 'last_epoch') if not last_received: self.kv.store('fastcast', 'last_epoch', {'last': 0}) if not self.kv.exists('fastcast', 'address'): pub, priv = generateKey() self.kv.store('fastcast', 'address', {"pub": pub, "priv": priv}) logging.info('fastcast pubkey: %r' % self.kv.get_by_section_key('fastcast', 'address')['pub'])
def __init__(self): self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self) last_received = self.kv.get_by_section_key('fastcast', 'last_epoch') if not last_received: self.kv.store('fastcast', 'last_epoch', {'last':0}) self.set_fastcast_address()
def __init__(self): self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self) last_received = self.kv.get_by_section_key('fastcast', 'last_epoch') if not last_received: self.kv.store('fastcast', 'last_epoch', {'last':0}) if not self.kv.exists('fastcast', 'address'): pub, priv = generateKey() self.kv.store('fastcast', 'address', {"pub": pub, "priv": priv}) logging.info('fastcast pubkey: %r' % self.kv.get_by_section_key('fastcast', 'address')['pub'])
class Oracle: def __init__(self): self.communication = OracleCommunication() self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self) def handle_request(self, request): logging.debug(request) operation, message = request if not operation in self.handlers: logging.debug("operation {} not supported".format(operation)) return handler = self.handlers[operation] try: message.message = json.loads(message.message) if 'message_id' in message.message: logging.info('parsing message_id: %r' % message.message['message_id']) handler(self).handle_request(message) except: logging.debug(message) logging.exception('error handling the request') # Save object to database for future reference db_class = self.db.operations[operation] if db_class: db_class(self.db).save(message) def handle_task(self, task): operation = task['operation'] assert(operation in self.handlers) handler = self.handlers[operation] handler(self).handle_task(task) operation = task['operation'] handler = self.handlers[operation] if handler: if handler(self).valid_task(task): return task else: logging.debug('Task marked as invalid by handler') self.task_queue.done(task) return None else: logging.debug("Task has invalid operation") self.task_queue.done(task) return None def is_fee_sufficient(self, addr, fee): if addr != self.oracle_address: return False if fee < Decimal(ORACLE_FEE): return False return True def run(self): if not ORACLE_ADDRESS: self.oracle_address = self.kv.get_by_section_key('config','ORACLE_ADDRESS') if self.oracle_address is None: new_addr = self.btc.server.getnewaddress() self.oracle_address = new_addr logging.error("created a new address: '%s'" % new_addr) self.kv.store('config','ORACLE_ADDRESS',new_addr) else: self.oracle_address = ORACLE_ADDRESS logging.info("my multisig address is %s" % self.oracle_address) logging.info( "my pubkey: %r" % self.btc.validate_address(self.oracle_address)['pubkey'] ) logging.debug("awaiting requests...") count = 0 while True: # Proceed all requests requests = self.communication.get_new_requests() if len(requests) == 0: count = count + 1 if count > 30: logging.debug("{0} new requests".format(len(requests))) count = 0 else: logging.debug("{0} new requests".format(len(requests))) for request in requests: self.handle_request(request) self.communication.mark_request_done(request) task = self.task_queue.get_oldest_task() while task is not None: self.handle_task(task) self.task_queue.done(task) task = self.task_queue.get_oldest_task() time.sleep(1)
def pricecheck_create(args): if len(args)<5: print "USAGE: `%s pricecheck_create <pubkey_once> <locktime_minutes> <return_address_if_greater> <return_address_if_smaller> <value>`" % START_COMMAND print "- run `%s main` to obtain pubkey_once" % START_COMMAND print "- keep in mind that this is alpha, don't expect oracles to run properly for any extended periods of time" return btc = BitcoinClient() request = {} client_pubkey = args[0] request['locktime'] = time.time() + int(args[1])*60 request['return_if_greater'] = args[2] request['return_if_lesser'] = args[3] request['price'] = float(args[4]) print "fetching charter url" # hopefully it didn't check between running main1 and main2 charter = fetch_charter(CHARTER_URL) oracle_pubkeys = [] oracle_fees = {} oracle_bms = [] for o in charter['nodes']: oracle_pubkeys.append(o['pubkey']) oracle_fees[o['address']] = o['fee'] oracle_bms.append(o['bm']) oracle_fees[charter['org_address']] = charter['org_fee'] min_sigs = int(ceil(float(len(oracle_pubkeys))/2)) key_list = [client_pubkey] + oracle_pubkeys response = btc.create_multisig_address(min_sigs, key_list) msig_addr = response['address'] # we're using this as an identificator redeemScript = response['redeemScript'] request['message_id'] = "%s-%s" % (msig_addr, str(randrange(1000000000,9000000000))) request['pubkey_list'] = key_list request['miners_fee_satoshi'] = MINERS_FEE print "fetching transactions incoming to %s ..." % msig_addr # for production purposes you might want to fetch the data using bitcoind, but that's expensive address_json = liburl_wrapper.safe_read("https://blockchain.info/address/%s?format=json" % msig_addr, timeout_time=10) try: address_history = json.loads(address_json) except: print "blockchain.info problem" print address_json return prevtxs = [] sum_satoshi = 0 for tx in address_history['txs']: outputs = [] if 'out' in tx: outputs = outputs + tx['out'] if 'outputs' in tx: outputs = outputs + tx['outputs'] for vout in tx['out']: print vout if vout['addr'] == msig_addr: prevtx = { 'scriptPubKey' : vout['script'], 'vout': vout['n'], 'txid': tx['hash'], 'redeemScript': redeemScript, } sum_satoshi += vout['value'] prevtxs.append(prevtx) if len(prevtxs) == 0: print "ERROR: couldn't find transactions sending money to %s" % msig_addr return request['prevtxs'] = prevtxs request['outputs'] = oracle_fees request["req_sigs"] = min_sigs request['operation'] = 'pricecheck_create' request['sum_satoshi'] = sum_satoshi bm = BitmessageClient() print "sending: %r" % json.dumps(request) print bm.chan_address request_content = json.dumps(request) print bm.send_message(bm.chan_address, request['operation'], request_content)
def main2(args): if len(args) < 3: print "USAGE: `%s main2 <pubkey_once> <locktime_minutes> <return_address>`" % START_COMMAND print "- run `%s main` to obtain pubkey_once" % START_COMMAND print "- keep in mind that this is alpha, don't expect oracles to run properly for any extended periods of time" return btc = BitcoinClient() request = {} client_pubkey = args[0] request['locktime'] = time.time() + int(args[1]) * 60 request['return_address'] = args[2] print "fetching charter url" # hopefully it didn't check between running main1 and main2 charter = fetch_charter(CHARTER_URL) oracle_pubkeys = [] oracle_fees = {} oracle_bms = [] for o in charter['nodes']: oracle_pubkeys.append(o['pubkey']) oracle_fees[o['address']] = o['fee'] oracle_bms.append(o['bm']) oracle_fees[charter['org_address']] = charter['org_fee'] min_sigs = int(ceil(float(len(oracle_pubkeys)) / 2)) key_list = [client_pubkey] + oracle_pubkeys response = btc.create_multisig_address(min_sigs, key_list) msig_addr = response['address'] # we're using this as an identificator redeemScript = response['redeemScript'] request['message_id'] = "%s-%s" % (msig_addr, str(randrange(1000000000, 9000000000))) request['pubkey_list'] = key_list request['miners_fee_satoshi'] = MINERS_FEE print "fetching transactions incoming to %s ..." % msig_addr # for production purposes you might want to fetch the data using bitcoind, but that's expensive address_json = liburl_wrapper.safe_read( "https://blockchain.info/address/%s?format=json" % msig_addr, timeout_time=10) try: address_history = json.loads(address_json) except: print "blockchain.info problem" print address_json return prevtxs = [] sum_satoshi = 0 for tx in address_history['txs']: outputs = [] if 'out' in tx: outputs = outputs + tx['out'] if 'outputs' in tx: outputs = outputs + tx['outputs'] for vout in tx['out']: print vout if vout['addr'] == msig_addr: prevtx = { 'scriptPubKey': vout['script'], 'vout': vout['n'], 'txid': tx['hash'], 'redeemScript': redeemScript, } sum_satoshi += vout['value'] prevtxs.append(prevtx) if len(prevtxs) == 0: print "ERROR: couldn't find transactions sending money to %s" % msig_addr return request['prevtxs'] = prevtxs request['outputs'] = oracle_fees request["req_sigs"] = min_sigs request['operation'] = 'timelock_create' request['sum_satoshi'] = sum_satoshi bm = BitmessageClient() print "sending: %r" % json.dumps(request) print bm.chan_address request_content = json.dumps(request) print bm.send_message(bm.chan_address, request['operation'], request_content) print "" print "Gathering oracle responses. It may take BitMessage 30-60 seconds to deliver a message one way." print "Although we've seen delays up to half an hour, especially if BitMessage client was just launched." print "" oracles_confirmed = 0 while oracle_bms: messages = bm.get_unread_messages() print "oracles confirmed: {}".format(oracles_confirmed) for msg in messages: if msg.from_address in oracle_bms: try: content = json.loads(msg.message) except: print msg.message print 'failed decoding message' continue if 'in_reply_to' not in content: continue if content['in_reply_to'] == request['message_id']: print "[%r][%r] %r" % (msg.subject, msg.from_address, msg.message) print "" oracle_bms.remove(msg.from_address) if oracle_bms: #if still awaiting replies from some oracles time.sleep(10)
def main2(args): if len(args) < 3: print "USAGE: `%s main2 <pubkey_once> <locktime_minutes> <return_address>`" % START_COMMAND print "- run `%s main` to obtain pubkey_once" % START_COMMAND print "- keep in mind that this is alpha, don't expect oracles to run properly for any extended periods of time" return btc = BitcoinClient() request = {} client_pubkey = args[0] request['locktime'] = time.time() + int(args[1]) * 60 request['return_address'] = args[2] print "fetching charter url" # hopefully it didn't check between running main1 and main2 charter = fetch_charter(CHARTER_URL) oracle_pubkeys = [] oracle_fees = {} oracle_bms = [] for o in charter['nodes']: oracle_pubkeys.append(o['pubkey']) oracle_fees[o['address']] = o['fee'] #oracle_bms.append(o['bm']) oracle_fees[charter['org_address']] = charter['org_fee'] min_sigs = int(ceil(float(len(oracle_pubkeys)) / 2)) key_list = [client_pubkey] + oracle_pubkeys response = btc.create_multisig_address(min_sigs, key_list) msig_addr = response['address'] # we're using this as an identificator redeemScript = response['redeemScript'] request['message_id'] = "%s-%s" % (msig_addr, str(randrange(1000000000, 9000000000))) request['pubkey_list'] = key_list request['miners_fee_satoshi'] = MINERS_FEE print "fetching transactions incoming to %s ..." % msig_addr import requests # for production purposes you might want to fetch the data using bitcoind, but that's expensive print "get" address_json = requests.get( "https://blockchain.info/address/%s?format=json" % msig_addr).text #try: print address_json address_history = json.loads(address_json) #except: #print "blockchain.info problem" #print address_json #return prevtxs = [] sum_satoshi = 0 for tx in address_history['txs']: outputs = [] if 'out' in tx: outputs = outputs + tx['out'] if 'outputs' in tx: outputs = outputs + tx['outputs'] for vout in tx['out']: print vout if vout['addr'] == msig_addr: prevtx = { 'scriptPubKey': vout['script'], 'vout': vout['n'], 'txid': tx['hash'], 'redeemScript': redeemScript, } sum_satoshi += vout['value'] prevtxs.append(prevtx) if len(prevtxs) == 0: print "ERROR: couldn't find transactions sending money to %s" % msig_addr # return request['prevtxs'] = prevtxs request['outputs'] = oracle_fees request["req_sigs"] = min_sigs request['operation'] = 'timelock_create' request['sum_satoshi'] = sum_satoshi pub, priv = generateKey() meta_request = {} meta_request['source'] = pub meta_request['channel'] = 0 meta_request['signature'] = 0 meta_request['body'] = json.dumps(request) print sendMessage(constructMessage(priv, **meta_request))
def main2(args): if len(args)<3: print "USAGE: `%s main2 <pubkey_once> <locktime_minutes> <return_address>`" % START_COMMAND print "- run `%s main` to obtain pubkey_once" % START_COMMAND print "- keep in mind that this is alpha, don't expect oracles to run properly for any extended periods of time" return btc = BitcoinClient() request = {} client_pubkey = args[0] request['locktime'] = time.time() + int(args[1])*60 request['return_address'] = args[2] print "fetching charter url" # hopefully it didn't check between running main1 and main2 charter = fetch_charter(CHARTER_URL) oracle_pubkeys = [] oracle_fees = {} oracle_bms = [] for o in charter['nodes']: oracle_pubkeys.append(o['pubkey']) oracle_fees[o['address']] = o['fee'] oracle_bms.append(o['bm']) oracle_fees[charter['org_address']] = charter['org_fee'] min_sigs = int(ceil(float(len(oracle_pubkeys))/2)) key_list = [client_pubkey] + oracle_pubkeys response = btc.create_multisig_address(min_sigs, key_list) msig_addr = response['address'] # we're using this as an identificator redeemScript = response['redeemScript'] request['message_id'] = "%s-%s" % (msig_addr, str(randrange(1000000000,9000000000))) request['pubkey_list'] = key_list request['miners_fee_satoshi'] = MINERS_FEE print "fetching transactions incoming to %s ..." % msig_addr # for production purposes you might want to fetch the data using bitcoind, but that's expensive address_json = liburl_wrapper.safe_read("https://blockchain.info/address/%s?format=json" % msig_addr, timeout_time=10) try: address_history = json.loads(address_json) except: print "blockchain.info problem" print address_json return prevtxs = [] sum_satoshi = 0 for tx in address_history['txs']: outputs = [] if 'out' in tx: outputs = outputs + tx['out'] if 'outputs' in tx: outputs = outputs + tx['outputs'] for vout in tx['out']: print vout if vout['addr'] == msig_addr: prevtx = { 'scriptPubKey' : vout['script'], 'vout': vout['n'], 'txid': tx['hash'], 'redeemScript': redeemScript, } sum_satoshi += vout['value'] prevtxs.append(prevtx) if len(prevtxs) == 0: print "ERROR: couldn't find transactions sending money to %s" % msig_addr return request['prevtxs'] = prevtxs request['outputs'] = oracle_fees request["req_sigs"] = min_sigs request['operation'] = 'timelock_create' request['sum_satoshi'] = sum_satoshi bm = BitmessageClient() print "sending: %r" % json.dumps(request) print bm.chan_address request_content = json.dumps(request) print bm.send_message(bm.chan_address, request['operation'], request_content) print "" print "Gathering oracle responses. It may take BitMessage 30-60 seconds to deliver a message one way." print "Although we've seen delays up to half an hour, especially if BitMessage client was just launched." print "" oracles_confirmed = 0 while oracle_bms: messages = bm.get_unread_messages() print "oracles confirmed: {}".format(oracles_confirmed) for msg in messages: if msg.from_address in oracle_bms: try: content = json.loads(msg.message) except: print msg.message print 'failed decoding message' continue if 'in_reply_to' not in content: continue if content['in_reply_to'] == request['message_id']: print "[%r][%r] %r" % (msg.subject, msg.from_address, msg.message) print "" oracle_bms.remove(msg.from_address) if oracle_bms: #if still awaiting replies from some oracles time.sleep(10)
class Oracle: def __init__(self): self.communication = OracleCommunication() self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self) def handle_request(self, request): logging.debug(request) operation, message = request if not operation in self.handlers: logging.debug("operation {} not supported".format(operation)) return handler = self.handlers[operation] try: message.message = json.loads(message.message) if 'message_id' in message.message: logging.info('parsing message_id: %r' % message.message['message_id']) handler(self).handle_request(message) except: logging.debug(message) logging.exception('error handling the request') # Save object to database for future reference db_class = self.db.operations[operation] if db_class: db_class(self.db).save(message) def handle_task(self, task): operation = task['operation'] assert (operation in self.handlers) handler = self.handlers[operation] handler(self).handle_task(task) operation = task['operation'] handler = self.handlers[operation] if handler: if handler(self).valid_task(task): return task else: logging.debug('Task marked as invalid by handler') self.task_queue.done(task) return None else: logging.debug("Task has invalid operation") self.task_queue.done(task) return None def is_fee_sufficient(self, addr, fee): if addr != self.oracle_address: return False if fee < Decimal(ORACLE_FEE): return False return True def run(self): if not ORACLE_ADDRESS: self.oracle_address = self.kv.get_by_section_key( 'config', 'ORACLE_ADDRESS') if self.oracle_address is None: new_addr = self.btc.server.getnewaddress() self.oracle_address = new_addr logging.error("created a new address: '%s'" % new_addr) self.kv.store('config', 'ORACLE_ADDRESS', new_addr) else: self.oracle_address = ORACLE_ADDRESS logging.info("my multisig address is %s" % self.oracle_address) logging.info("my pubkey: %r" % self.btc.validate_address(self.oracle_address)['pubkey']) logging.debug("awaiting requests...") count = 0 while True: # Proceed all requests requests = self.communication.get_new_requests() if len(requests) == 0: count = count + 1 if count > 30: logging.debug("{0} new requests".format(len(requests))) count = 0 else: logging.debug("{0} new requests".format(len(requests))) for request in requests: self.handle_request(request) self.communication.mark_request_done(request) task = self.task_queue.get_oldest_task() while task is not None: self.handle_task(task) self.task_queue.done(task) task = self.task_queue.get_oldest_task() time.sleep(1)
class Oracle: def __init__(self): self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self) last_received = self.kv.get_by_section_key('fastcast', 'last_epoch') if not last_received: self.kv.store('fastcast', 'last_epoch', {'last':0}) self.set_fastcast_address() def set_fastcast_address(self): if self.kv.exists('fastcast', 'address'): return pub, priv = generateKey() self.kv.store('fastcast', 'address', {"pub": pub, "priv": priv}) def broadcast_with_fastcast(self, message): data = self.kv.get_by_section_key('fastcast', 'address') pub = data['pub'] priv = data['priv'] broadcastMessage(message, pub, priv) def handle_request(self, request): logging.debug(request) operation, message = request if not operation in self.handlers: logging.debug("operation {} not supported".format(operation)) return handler = self.handlers[operation] try: message.message = json.loads(message.message) if 'message_id' in message.message: logging.info('parsing message_id: %r' % message.message['message_id']) handler(self).handle_request(message) except: logging.debug(message) logging.exception('error handling the request') # Save object to database for future reference db_class = self.db.operations[operation] if db_class: db_class(self.db).save(message) def get_last_block_number(self): val = KeyValue(self.db).get_by_section_key('blocks', 'last_block_number') if not val: return 0 last_block = val['last_block'] return last_block def set_last_block(self): last_block_number = self.btc.get_block_count() # We need to satisfy a condition on looking only for blocks with at # least CONFIRMATIONS of confirmations satisfied = False while not satisfied: block_hash = self.btc.get_block_hash(last_block_number) block = self.btc.get_block(block_hash) if block['confirmations'] >= CONFIRMATIONS: satisfied = True else: last_block_number -= 1 KeyValue(self.db).store('blocks', 'last_block_number', {'last_block':last_block_number}) return last_block_number def get_new_block(self): last_block_number = self.get_last_block_number() if last_block_number == 0: last_block_number = self.set_last_block() newer_block = last_block_number + 1 block_hash = self.btc.get_block_hash(newer_block) if not block_hash: return None block = self.btc.get_block(block_hash) # We are waiting for enough confirmations if block['confirmations'] < CONFIRMATIONS: return None logging.info("New block {}".format(newer_block)) return block def handle_task(self, task): operation = task['operation'] assert(operation in self.handlers) handler = self.handlers[operation] handler(self).handle_task(task) operation = task['operation'] handler = self.handlers[operation] if handler: if handler(self).valid_task(task): return task else: logging.debug('Task marked as invalid by handler') self.task_queue.done(task) return None else: logging.debug("Task has invalid operation") self.task_queue.done(task) return None def is_fee_sufficient(self, addr, fee): if addr != self.oracle_address: return False if fee < Decimal(ORACLE_FEE): return False return True def prepare_request(self, request): try: fmsg = FastcastMessage(request) except: raise FastcastProtocolError() msg_body = json.loads(fmsg.message) if not 'operation' in msg_body: raise MissingOperationError() operation = msg_body['operation'] return (operation, fmsg) def filter_requests(self, old_req): new_req = [] last_received = self.kv.get_by_section_key('fastcast','last_epoch')['last'] max_received = last_received for r in old_req: received_epoch = int(r['epoch']) if received_epoch > last_received: new_req.append(r) max_received = max(max_received, received_epoch) if len(new_req) > 0: self.kv.update('fastcast', 'last_epoch', {'last':max_received}) return new_req def run(self): if not ORACLE_ADDRESS: self.oracle_address = self.kv.get_by_section_key('config','ORACLE_ADDRESS') if self.oracle_address is None: new_addr = self.btc.server.getnewaddress() self.oracle_address = new_addr logging.error("created a new address: '%s'" % new_addr) self.kv.store('config','ORACLE_ADDRESS',new_addr) else: self.oracle_address = ORACLE_ADDRESS logging.info("my multisig address is %s" % self.oracle_address) logging.info( "my pubkey: %r" % self.btc.validate_address(self.oracle_address)['pubkey'] ) while True: # Proceed all requests requests = getMessages() requests = requests['results'] requests = self.filter_requests(requests) for prev_request in requests: try: request = self.prepare_request(prev_request) except MissingOperationError: logging.info('message doesn\'t have operation field, invalid') logging.info(prev_request) continue except FastcastProtocolError: logging.info('message does not have all required fields') logging.info(prev_request) continue self.handle_request(request) task = self.task_queue.get_oldest_task() while task is not None: self.handle_task(task) self.task_queue.done(task) task = self.task_queue.get_oldest_task() new_block = self.get_new_block() if new_block: handlers = op_handlers.itervalues() # Every available handler should get a chance to handle new block for h in handlers: h(self).handle_new_block(new_block) KeyValue(self.db).update('blocks', 'last_block_number', {'last_block':new_block['height']}) time.sleep(1)
class Oracle: def __init__(self): self.db = OracleDb() self.btc = BitcoinClient() self.kv = KeyValue(self.db) self.task_queue = TaskQueue(self.db) self.handlers = op_handlers self.signer = TransactionSigner(self) last_received = self.kv.get_by_section_key('fastcast', 'last_epoch') if not last_received: self.kv.store('fastcast', 'last_epoch', {'last': 0}) if not self.kv.exists('fastcast', 'address'): pub, priv = generateKey() self.kv.store('fastcast', 'address', {"pub": pub, "priv": priv}) logging.info('fastcast pubkey: %r' % self.kv.get_by_section_key('fastcast', 'address')['pub']) def broadcast_with_fastcast(self, message): data = self.kv.get_by_section_key('fastcast', 'address') pub = data['pub'] priv = data['priv'] broadcastMessage(message, pub, priv) def handle_request(self, request): logging.debug(request) operation, message = request if not operation in self.handlers: logging.debug("operation {} not supported".format(operation)) return handler = self.handlers[operation] try: message.message = json.loads(message.message) if 'message_id' in message.message: logging.info('parsing message_id: %r' % message.message['message_id']) handler(self).handle_request(message) except: logging.debug(message) logging.exception('error handling the request') # Save object to database for future reference db_class = self.db.operations[operation] if db_class: db_class(self.db).save(message) def get_last_block_number(self): val = KeyValue(self.db).get_by_section_key('blocks', 'last_block_number') if not val: return 0 last_block = val['last_block'] return last_block def set_last_block(self): last_block_number = self.btc.get_block_count() # We need to satisfy a condition on looking only for blocks with at # least CONFIRMATIONS of confirmations satisfied = False while not satisfied: block_hash = self.btc.get_block_hash(last_block_number) block = self.btc.get_block(block_hash) if block['confirmations'] >= CONFIRMATIONS: satisfied = True else: last_block_number -= 1 print "not enough confirmations - checking previous block %r" % last_block_number KeyValue(self.db).store('blocks', 'last_block_number', {'last_block': last_block_number}) return last_block_number def get_new_block(self): last_block_number = self.get_last_block_number() logging.debug("last_block_number: %r" % last_block_number) if last_block_number == 0: last_block_number = self.set_last_block() newer_block = last_block_number + 1 block_hash = self.btc.get_block_hash(newer_block) logging.info("block hash: %r" % block_hash) if not block_hash: return None block = self.btc.get_block(block_hash) # We are waiting for enough confirmations if block['confirmations'] < CONFIRMATIONS: return None logging.info("New block {}".format(newer_block)) return block def handle_task(self, task): operation = task['operation'] assert (operation in self.handlers) handler = self.handlers[operation] handler(self).handle_task(task) operation = task['operation'] handler = self.handlers[operation] if handler: if handler(self).valid_task(task): return task else: logging.debug('Task marked as invalid by handler') self.task_queue.done(task) return None else: logging.debug("Task has invalid operation") self.task_queue.done(task) return None def is_fee_sufficient(self, addr, fee): if addr != self.oracle_address: return False if fee < Decimal(ORACLE_FEE): return False return True def prepare_request(self, request): try: fmsg = FastcastMessage(request) except: raise FastcastProtocolError() msg_body = json.loads(fmsg.message) if not 'operation' in msg_body: raise MissingOperationError() operation = msg_body['operation'] return (operation, fmsg) def filter_requests(self, old_req): new_req = [] last_received = self.kv.get_by_section_key('fastcast', 'last_epoch')['last'] max_received = last_received for r in old_req: received_epoch = int(r['epoch']) if received_epoch > last_received: new_req.append(r) max_received = max(max_received, received_epoch) if len(new_req) > 0: self.kv.update('fastcast', 'last_epoch', {'last': max_received}) return new_req def run(self): if not ORACLE_ADDRESS: self.oracle_address = self.kv.get_by_section_key( 'config', 'ORACLE_ADDRESS') if self.oracle_address is None: new_addr = self.btc.server.getnewaddress() self.oracle_address = new_addr logging.error("created a new address: '%s'" % new_addr) self.kv.store('config', 'ORACLE_ADDRESS', new_addr) else: self.oracle_address = ORACLE_ADDRESS logging.info("my fee: %r" % ORACLE_FEE) logging.info("my bitcoin address is %s" % self.oracle_address) logging.info("my bitcoin pubkey: %r" % self.btc.validate_address(self.oracle_address)['pubkey']) while True: # Proceed all requests requests = getMessages() requests = requests['results'] requests = self.filter_requests(requests) for prev_request in requests: try: request = self.prepare_request(prev_request) except MissingOperationError: logging.info( 'message doesn\'t have operation field, invalid') logging.info(prev_request) continue except FastcastProtocolError: logging.info('message does not have all required fields') logging.info(prev_request) continue self.handle_request(request) task = self.task_queue.get_oldest_task() while task is not None: self.handle_task(task) self.task_queue.done(task) task = self.task_queue.get_oldest_task() try: new_block = self.get_new_block() except: new_block = None logging.exception('problematic block!') if new_block: handlers = op_handlers.iteritems() addresses_per_handler = {} all_addresses = set() # Every handler can wait for transactions occuring on some addresses for name, handler in handlers: addresses = handler(self).get_observed_addresses() addresses_per_handler[name] = addresses for address in addresses: all_addresses.add(address) transactions = self.btc.get_transactions_from_block( new_block, list(all_addresses)) handlers = op_handlers.iteritems() for name, handler in handlers: addresses = addresses_per_handler[name] handler_transactions = [] for address in addresses: if address in transactions: handler_transactions.extend(transactions[address]) handler(self).handle_new_transactions(handler_transactions) KeyValue(self.db).update('blocks', 'last_block_number', {'last_block': new_block['height']}) time.sleep(10)