def counterparty_post(payload): url = 'http://{}:{}/api/'.format(config.RPC_CONNECT, config.RPC_PORT) auth = HTTPBasicAuth(config.RPC_USER, config.RPC_PASSWORD) headers = {'content-type': 'application/json'} try: response = requests.post(url=url, data=json.dumps(payload), auth=auth, headers=headers) except (requests.exceptions.SSLError, requests.exceptions.Timeout, requests.exceptions.ConnectionError, requests.exceptions.HTTPError) as e: raise MessageProducerError(e) if response is None: raise MessageProducerError('Cannot communicate with {}'.format(url)) elif response.status_code not in (200, 500): msg = str( response.status_code) + ' ' + response.reason + ' ' + response.text raise MessageProducerError(msg) response_json = response.json() if 'error' not in response_json.keys() or response_json['error'] == None: response = response_json['result'] else: raise MessageProducerError(response_json['error']) return response
def create_issuance(**kwargs): """ :param kwargs: { "redisData": { "redisChannel": "id" }, "input": { "method": "issuance", "params": { "source": "PhGWKbvaTdU7MMgmuktGCaVQN93Rxvye7d", "quantity": 10000000000, "asset": "TRUEBTC5", "description": "Some desc here" } } } :return: signed tx hex """ required_params = ["asset", "source", "quantity", "description"] # STEP 1: validation # TODO: Improve validation # 1.1 check for required params for required_param in required_params: if required_param not in kwargs: raise MessageProducerError( "Following parameter `{}` is required".format(required_param)) # 1.2 check for registered address ico = Ico() ico_info = ico.get_by_source_address(kwargs.get('source')) if not ico_info: # not registered attempt raise MessageProducerError( "Following address `{}` is not registered as ICO address".format( kwargs.get('source'))) if not ico_info['is_approved']: raise MessageProducerError( "Following ICO {} on `{}` address is not approved".format( ico_info['asset'], kwargs.get('source'))) # STEP 2: create_issuance in party payload = { 'method': 'create_issuance', 'params': kwargs, 'jsonrpc': '2.0', 'id': 0 } # unsigned tx hex unsigned_tx_hex = counterparty_post(payload) # STEP 3: Sign and Send to the paicoin blockchain signed_tx_hex = sign_and_send(unsigned_tx_hex) return signed_tx_hex
def send_asset(**kwargs): """ :param kwargs: { "redisData": { "redisChannel": "id" }, "input": { "method": "send", "params": { "source": "PhGWKbvaTdU7MMgmuktGCaVQN93Rxvye7d", "destination": "PhGWKbvaTdU7MMgmuktGCaVQN93Rxvye7d", "quantity": 10, "asset": "TRUEBTC5", } } } :return: signed tx hex """ send_params = ['source', 'destination', 'asset', 'quantity'] if not all(k in kwargs for k in send_params): raise MessageProducerError( "Following parameters {} are required".format(send_params)) payload = { 'method': 'create_send', 'params': kwargs, 'jsonrpc': '2.0', 'id': 0 } # TODO: add response validation unsigned_tx_hex = counterparty_post(payload) return sign_and_send(unsigned_tx_hex)
def get_message_body(message): try: message_data = json.loads(message) except ValueError: raise MessageProducerError( "Incoming message couldn't not be json parsed") return message_data
def _validate_incoming_message(self, message_data): for field_id in self.root_nodes: if field_id not in message_data: msg = "Incoming message does not contain {} field".format( field_id) raise MessageProducerError(msg) if len(message_data) != len(self.root_nodes): msg = "Incoming message contains unspecified fields.\nIt should contain only {}".format( ', '.join(self.root_nodes)) raise MessageProducerError(msg) if 'redisChannel' not in message_data['redisData']: raise MessageProducerError( "Incoming message redisData must contain redisChannel field") if 'method' not in message_data['input']: raise MessageProducerError( "Incoming message input must contain method name")
def process_message(self, message): self.error = False self.logger.info(message) message_body = self.get_message_body(message) self._validate_incoming_message(message_body) redis_channel = message_body['redisData']['redisChannel'] self._status_reporting_provider.set_publish_channel(redis_channel) method = message_body['input']['method'] params = message_body['input']['params'] # TODO: Process message if method == 'register_ico': # returns source address source_address = register_ico(**params) response = {'source_address': source_address} elif method == 'list_ico': # list of all created icos # TODO: add minimum filter params list_ico = get_all_icos() response = {'all_ico': list_ico} elif method == 'get_ico_info': # returns info about ico ico_info = get_ico_info(id=params['id']) response = {'ico_info': ico_info} elif method == 'issuance': # do issuance signed_hex = create_issuance(**params) response = {'signed_tx_hex': signed_hex} elif method == 'get_unpaid_transactions': # returns unpaid ICO transactions unpaid_transactions = get_unpaid_transactions(unpaid=True) response = {'unpaid_transactions': unpaid_transactions} elif method == 'send': # send asset response = send_asset(**params) else: self.logger.error("Invalid method {}, use {} instead".format( method, self._allowed_methods)) raise MessageProducerError( "Invalid method {}, use {} instead".format( method, self._allowed_methods)) self.publish_processing_status(100, 'Finished worker', response)
def sign_and_send(unsigned_tx_hex): sign_payload = { 'method': 'signrawtransaction', 'params': [unsigned_tx_hex] } # FIXME: Update responses in paicoin response = paicoin_post(sign_payload) if response.status_code in [200, 500]: response_json = response.json() signed_hex = response_json['result']['hex'] # STEP 3.2: send raw transaction send_payload = {'method': 'sendrawtransaction', 'params': [signed_hex]} response = paicoin_post(send_payload) if response.status_code in [200, 500]: response_json = response.json() tx_id = response_json['result'] return tx_id else: raise MessageProducerError( 'Could not communicate with paicoin rpc server')
def register_ico(**kwargs): """ :param message: { "redisData": { "redisChannel": "id" }, "input": { "method": "register_ico", "params": { "return_address": "PiJgSZgm2z5EhBg4msPXCxL3W2DMhP9ihV", "quantity": 10000000, "asset": "TRUEBTC", "price": 1, "start_date": "2018-08-31 12:00:00", "end_date": "2018-08-31 12:00:00", "hard_cap": 1000000000, "soft_cap": 100000000000, "details": { "description": "Some TRUE description" } } } } :return: address (?????) """ # TODO: Add more parameters required_params = [ 'return_address', 'asset', 'quantity', 'price', 'start_date', 'end_date', 'hard_cap', 'soft_cap' ] # STEP 1: validation # TODO: Improve validation # check for required params for required_param in required_params: if required_param not in kwargs: raise MessageProducerError( "Following parameter `{}` is required".format(required_param)) # STEP 2: Generate associated address for income transactions in smartnode paicoin address = generate_address(kwargs.get('return_address')) # STEP 3: Save to ICO model params = { 'asset': kwargs.get('asset'), 'return_address': kwargs.get('return_address'), 'source_address': address, 'details': json.dumps(kwargs.get('details')) if kwargs.get('details') else None } try: ico = Ico() ico.save_ico(**params) except sqlalchemy.exc.IntegrityError as e: raise MessageProducerError(e) # STEP 4: Create config if not os.path.exists(ICO_CONFIG_DIR): os.makedirs(ICO_CONFIG_DIR) ico_config_path = os.path.join(ICO_CONFIG_DIR, address) if not os.path.exists(ico_config_path): os.mkdir(ico_config_path) # config ico_config = { 'required_parameters': { 'asset': kwargs.get('asset'), 'quantity': kwargs.get('quantity'), 'description': kwargs.get('description'), 'source_address': address, 'return_address': kwargs.get('return_address'), 'price': kwargs.get('price'), 'start_date': kwargs.get('start_date'), 'end_date': kwargs.get('start_date'), 'auto_payments': kwargs.get('auto_payments') if kwargs.get('auto_payments') else True, }, 'optional_parameters': { 'soft_cap': kwargs.get('soft_cap'), 'hard_cap': kwargs.get('hard_cap'), 'asset_long_name': kwargs.get('asset_long_name') }, 'additional_parameters': { 'details': kwargs.get('details') } } with open( os.path.join(ico_config_path, '{}.conf.json'.format(kwargs.get('asset'))), 'w+') as file: json.dump(ico_config, file, indent=4) return address