def setUpClass(cls): log = logging.getLogger("NodeRpcTest") log.debug("Setting up NodeRpcTest") DOTENV_FILE = '.env.test' env_config = Config(RepositoryEnv(DOTENV_FILE)) cls.network = env_config('NETWORK') cls.ela_to_use = env_config('ELA_TO_USE') cls.ela_eth_to_use = env_config('ELA_ETH_TO_USE') cls.did_to_use = env_config('DID_TO_USE') cls.api_key_to_use = get_api_from_did(cls.did_to_use) cls.private_key_to_use = env_config('PRIVATE_KEY_TO_USE')
def RpcMethod(self, request, context): metadata = dict(context.invocation_metadata()) did = metadata["did"] api_key = get_api_from_did(did) try: jwt_info = jwt.decode(request.input, key=api_key, algorithms=['HS256']).get('jwt_info') except Exception as e: status_message = 'Authentication Error' logging.debug(f"RpcMethod : {did} : {api_key} : {status_message} : {e}") return node_rpc_pb2.Response(output='', status_message=status_message, status=False) if type(jwt_info) == str: jwt_info = json.loads(jwt_info) # Check whether the user is able to use this API by checking their rate limiter response = self.rate_limiter.check_rate_limit(settings.NODE_RPC_LIMIT, api_key, self.RpcMethod.__name__) if response: status_message = f'Number of daily access limit exceeded {response["result"]["daily_limit"]}' logging.debug(f"{did} : {api_key} : {status_message}") return node_rpc_pb2.Response(output=json.dumps(response), status_message=status_message, status=False) network = jwt_info['network'] chain = jwt_info["chain"] method = jwt_info["method"] params = jwt_info["params"] d = {"method": method} if params: d["params"] = params if chain == "mainchain" and not ( method in settings.NODE_COMMON_RPC_METHODS or method in settings.NODE_MAIN_RPC_METHODS): status_message = f'The method {method} is not available for the chain {chain}' logging.debug(f"{api_key} : {status_message}") return node_rpc_pb2.Response(output=json.dumps({}), status_message=status_message, status=False) if chain != "mainchain" and chain != "eth" and method not in settings.NODE_COMMON_RPC_METHODS: status_message = f'The method {method} is not available for the chain {chain}' logging.debug(f"{api_key} : {status_message}") return node_rpc_pb2.Response(output=json.dumps({}), status_message=status_message, status=False) if chain == "eth" and method not in settings.NODE_SIDECHAIN_ETH_RPC_METHODS: status_message = f'The method {method} is not available for the chain {chain}' logging.debug(f"{api_key} : {status_message}") return node_rpc_pb2.Response(output=json.dumps({}), status_message=status_message, status=False) if network == "mainnet": if chain == "mainchain": url = config('MAIN_NET_MAINCHAIN_RPC_PORT') elif chain == "did": url = config('MAIN_NET_SIDECHAIN_DID_RPC_PORT') elif chain == "token": url = config('MAIN_NET_SIDECHAIN_TOKEN_RPC_PORT') elif chain == "eth": url = config('MAIN_NET_SIDECHAIN_ETH_RPC_PORT') d["id"] = 1 else: status_message = f'The chain {chain} is not supported for the network {network}' logging.debug(f"{api_key} : {status_message}") return node_rpc_pb2.Response(output=json.dumps({}), status_message=status_message, status=False) else: if chain == "mainchain": url = config('PRIVATE_NET_MAINCHAIN_RPC_PORT') elif chain == "did": url = config('PRIVATE_NET_SIDECHAIN_DID_RPC_PORT') elif chain == "token": url = config('PRIVATE_NET_SIDECHAIN_TOKEN_RPC_PORT') elif chain == "eth": url = config('PRIVATE_NET_SIDECHAIN_ETH_RPC_PORT') d["id"] = 1 else: status_message = f'The chain {chain} is not supported for the network {network}' logging.debug(f"{api_key} : {status_message}") return node_rpc_pb2.Response(output=json.dumps({}), status_message=status_message, status=False) response = requests.post(url, data=json.dumps(d), headers=self.headers, timeout=REQUEST_TIMEOUT) data = json.loads(response.text) # generate jwt token jwt_info = { 'result': data['result'] } jwt_token = jwt.encode({ 'jwt_info': jwt_info, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=settings.TOKEN_EXPIRATION) }, api_key, algorithm='HS256') return node_rpc_pb2.Response(output=jwt_token, status_message=f'Successfully called the method {method} for the chain {chain} in network {network}', status=True)
def UploadAndSign(self, request, context): metadata = dict(context.invocation_metadata()) did = metadata["did"] api_key = get_api_from_did(did) try: jwt_info = jwt.decode(request.input, key=api_key, algorithms=['HS256']).get('jwt_info') except Exception as e: status_message = 'Authentication Error' logging.debug( f"UploadAndSign : {did} : {api_key} : {status_message} : {e}") return hive_pb2.Response(output='', status_message=status_message, status=False) # reading input data if type(jwt_info) == str: # request from go package jwt_info = json.loads(jwt_info) network = jwt_info['network'] private_key = jwt_info['privateKey'] file_content = request.file_content # Check whether the user is able to use this API by checking their rate limiter response = self.rate_limiter.check_rate_limit( settings.UPLOAD_AND_SIGN_LIMIT, api_key, self.UploadAndSign.__name__) if response: status_message = f'Number of daily access limit exceeded {response["result"]["daily_limit"]}' logging.debug(f"{did} : {api_key} : {status_message}") return hive_pb2.Response(output=json.dumps(response), status_message=status_message, status=False) # checking file size if sys.getsizeof(file_content) > settings.FILE_UPLOAD_SIZE_LIMIT: status_message = "File size limit exceeded" logging.debug(f"{did} : {api_key} : {status_message}") return hive_pb2.Response(output="", status_message=status_message, status=False) if network == "mainnet": did_api_url = config( 'MAIN_NET_DID_SERVICE_URL') + settings.DID_SERVICE_API_SIGN hive_api_url = config( 'MAIN_NET_HIVE_PORT') + settings.HIVE_API_ADD_FILE else: did_api_url = config( 'PRIVATE_NET_DID_SERVICE_URL') + settings.DID_SERVICE_API_SIGN hive_api_url = config( 'PRIVATE_NET_HIVE_PORT') + settings.HIVE_API_ADD_FILE # encoding and encrypting key = get_encrypt_key(private_key) fernet = Fernet(key) file_content_encrypted = fernet.encrypt(file_content) # upload file to hive response = requests.get(hive_api_url, files={'file': file_content_encrypted}, headers=self.headers['hive'], timeout=REQUEST_TIMEOUT) data = json.loads(response.text) file_hash = data['Hash'] if not data: status_message = 'Error: File could not be uploaded' logging.debug(f"{did} : {api_key} : {status_message}") return hive_pb2.Response(output="", status_message=status_message, status=False) # signing the hash key req_data = {"privateKey": private_key, "msg": file_hash} response = requests.post(did_api_url, data=json.dumps(req_data), headers=self.headers['general'], timeout=REQUEST_TIMEOUT) data = json.loads(response.text) data['result']['hash'] = file_hash if data['status'] == 200: status_message = 'Successfully uploaded file to Elastos Hive' status = True else: status_message = 'Error' status = False del data['status'] # generate jwt token jwt_info = {'result': data['result']} jwt_token = jwt.encode( { 'jwt_info': jwt_info, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=settings.TOKEN_EXPIRATION) }, api_key, algorithm='HS256') return hive_pb2.Response(output=jwt_token, status_message=status_message, status=status)
def VerifyAndShow(self, request, context): metadata = dict(context.invocation_metadata()) did = metadata["did"] api_key = get_api_from_did(did) try: jwt_info = jwt.decode(request.input, key=api_key, algorithms=['HS256']).get('jwt_info') except Exception as e: status_message = 'Authentication Error' logging.debug( f"VerifyAndShow : {did} : {api_key} : {status_message} : {e}") return hive_pb2.Response(output='', status_message=status_message, status=False) if type(jwt_info) == str: jwt_info = json.loads(jwt_info) network = jwt_info['network'] signed_message = jwt_info['msg'] public_key = jwt_info['pub'] message_signature = jwt_info['sig'] message_hash = jwt_info['hash'] private_key = jwt_info['privateKey'] # Check whether the user is able to use this API by checking their rate limiter response = self.rate_limiter.check_rate_limit( settings.VERIFY_AND_SHOW_LIMIT, api_key, self.VerifyAndShow.__name__) if response: status_message = f'Number of daily access limit exceeded {response["result"]["daily_limit"]}' logging.debug(f"{did} : {api_key} : {status_message}") return hive_pb2.Response(output=json.dumps(response), status_message=status_message, status=False) if network == "mainnet": did_api_sign_url = config( 'MAIN_NET_DID_SERVICE_URL') + settings.DID_SERVICE_API_SIGN did_api_verify_url = config( 'MAIN_NET_DID_SERVICE_URL') + settings.DID_SERVICE_API_VERIFY hive_api_url = config( 'MAIN_NET_HIVE_PORT') + settings.HIVE_API_RETRIEVE_FILE + "{}" else: did_api_sign_url = config( 'PRIVATE_NET_DID_SERVICE_URL') + settings.DID_SERVICE_API_SIGN did_api_verify_url = config('PRIVATE_NET_DID_SERVICE_URL' ) + settings.DID_SERVICE_API_VERIFY hive_api_url = config('PRIVATE_NET_HIVE_PORT' ) + settings.HIVE_API_RETRIEVE_FILE + "{}" # verify the hash key json_data = { "msg": signed_message, "pub": public_key, "sig": message_signature } response = requests.post(did_api_verify_url, data=json.dumps(json_data), headers=self.headers['general'], timeout=REQUEST_TIMEOUT) data = json.loads(response.text) if not data['result']: return hive_pb2.Response( output="", status_message='Hash key could not be verified', status=False) # verify the given input message using private key req_data = {"privateKey": private_key, "msg": message_hash} response = requests.post(did_api_sign_url, data=json.dumps(req_data), headers=self.headers['general'], timeout=REQUEST_TIMEOUT) data = json.loads(response.text) if data['status'] != 200: status_message = 'Hash Key and message could not be verified' logging.debug(f"{did} : {api_key} : {status_message}") return hive_pb2.Response(output="", status_message=status_message, status=False) if data['result']['msg'] != signed_message: status_message = 'Hash Key and message could not be verified' logging.debug(f"{did} : {api_key} : {status_message}") return hive_pb2.Response(output="", status_message=status_message, status=False) # show content response = requests.get(hive_api_url.format(jwt_info['hash']), headers=self.headers['general'], timeout=REQUEST_TIMEOUT) # decrypt message key = get_encrypt_key(private_key) fernet = Fernet(key) decrypted_message = fernet.decrypt(response.text.encode()) # generate jwt token jwt_info = {} jwt_token = jwt.encode( { 'jwt_info': jwt_info, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=settings.TOKEN_EXPIRATION) }, api_key, algorithm='HS256') return hive_pb2.Response( output=jwt_token, file_content=decrypted_message, status_message='Successfully retrieved file from Elastos Hive', status=True)
def CreateWallet(self, request, context): metadata = dict(context.invocation_metadata()) did = metadata["did"] api_key = get_api_from_did(did) try: jwt_info = jwt.decode(request.input, key=api_key, algorithms=['HS256']).get('jwt_info') except Exception as e: status_message = 'Authentication Error' logging.debug( f"CreateWallet : {did} : {api_key} : {status_message} : {e}") return wallet_pb2.Response(output='', status_message=status_message, status=False) if type(jwt_info) == str: jwt_info = json.loads(jwt_info) network = jwt_info['network'] # Check whether the user is able to use this API by checking their rate limiter response = self.rate_limiter.check_rate_limit( settings.CREATE_WALLET_LIMIT, api_key, self.CreateWallet.__name__) if response: status_message = f'Number of daily access limit exceeded {response["result"]["daily_limit"]}' logging.debug(f"{did} : {api_key} : {status_message}") return wallet_pb2.Response(output=json.dumps(response), status_message=status_message, status=False) # Create wallets wallet_mainchain = create_wallet_mainchain(self.headers, network) if wallet_mainchain is None: status_message = 'Error: Mainchain wallet could not created' logging.debug(f"{did} : {api_key} : {status_message}") return wallet_pb2.Response(output="", status_message=status_message, status=False) wallet_sidechain_did = create_wallet_sidechain_did( self.headers, network) if wallet_sidechain_did is None: status_message = 'Error: DID Sidechain wallet could not created' logging.debug(f"{did} : {api_key} : {status_message}") return wallet_pb2.Response(output="", status_message=status_message, status=False) wallet_sidechain_token = wallet_mainchain wallet_sidechain_eth = create_wallet_sidechain_eth(network) if wallet_sidechain_eth is None: status_message = 'Error: Eth Sidechain wallet could not created' logging.debug(f"{did} : {api_key} : {status_message}") return wallet_pb2.Response(output="", status_message=status_message, status=False) # generate jwt token jwt_info = { 'result': { 'mainchain': { 'mnemonic': wallet_mainchain['mnemonic'], 'private_key': wallet_mainchain['private_key'], 'public_key': wallet_mainchain['public_key'], 'address': wallet_mainchain['address'] }, 'sidechain': { 'did': { 'mnemonic': wallet_sidechain_did['mnemonic'], 'private_key': wallet_sidechain_did['private_key'], 'public_key': wallet_sidechain_did['public_key'], 'address': wallet_sidechain_did['address'], 'did': wallet_sidechain_did['did'], }, 'token': { 'mnemonic': wallet_sidechain_token['mnemonic'], 'private_key': wallet_sidechain_token['private_key'], 'public_key': wallet_sidechain_token['public_key'], 'address': wallet_sidechain_token['address'], }, 'eth': { 'address': wallet_sidechain_eth['address'], 'private_key': wallet_sidechain_eth['private_key'] } }, } } jwt_token = jwt.encode( { 'jwt_info': jwt_info, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=settings.TOKEN_EXPIRATION) }, api_key, algorithm='HS256') return wallet_pb2.Response( output=jwt_token, status_message='Successfully created wallets', status=True)
def RequestELA(self, request, context): metadata = dict(context.invocation_metadata()) did = metadata["did"] api_key = get_api_from_did(did) try: jwt_info = jwt.decode(request.input, key=api_key, algorithms=['HS256']).get('jwt_info') except Exception as e: status_message = 'Authentication Error' logging.debug( f"RequestELA : {did} : {api_key} : {status_message} : {e}") return wallet_pb2.Response(output='', status_message=status_message, status=False) # Check whether the user is able to use this API by checking their rate limiter response = self.rate_limiter.check_rate_limit( settings.REQUEST_ELA_LIMIT, api_key, self.RequestELA.__name__) if response: status_message = f'Number of daily access limit exceeded {response["result"]["daily_limit"]}' logging.debug(f"{did} : {api_key} : {status_message}") return wallet_pb2.Response(output=json.dumps(response), status_message=status_message, status=False) if type(jwt_info) == str: jwt_info = json.loads(jwt_info) chain = jwt_info['chain'] address = jwt_info['address'] status_message = 'Successfully deposited ELA. Please wait 2-4 minutes for the ELA to arrive on main chain and ' \ '12-15 minutes to arrive on PoW sidechains' status = True amount = 5 if chain == "eth": currency_representation = "ELAETHSC" if len(WalletAddressesETH) < 10000: WalletAddressesETH.add(address) else: status_message = "Could not deposit ELA at this time. Please try again later" status = False else: if len(WalletAddresses) < 10000: WalletAddresses.add((chain, address)) else: status_message = "Could not deposit ELA at this time. Please try again later" status = False if chain == "mainchain": amount = 10 currency_representation = "ELA" if chain == "did": currency_representation = "ELADIDSC" elif chain == "token": currency_representation = "ELATOKENSC" # generate jwt token jwt_info = { 'result': { 'address': address, 'deposit_amount': "{0} {1}".format(amount, currency_representation) } } jwt_token = jwt.encode( { 'jwt_info': jwt_info, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=settings.TOKEN_EXPIRATION) }, api_key, algorithm='HS256') return wallet_pb2.Response(output=jwt_token, status_message=status_message, status=status)
def DeployEthContract(self, request, context): metadata = dict(context.invocation_metadata()) did = metadata["did"] api_key = get_api_from_did(did) try: jwt_info = jwt.decode(request.input, key=api_key, algorithms=['HS256']).get('jwt_info') except Exception as e: status_message = 'Authentication Error' logging.debug( f"DeployEthContract : {did} : {api_key} : {status_message} : {e}" ) return sidechain_eth_pb2.Response(output='', status_message=status_message, status=False) if type(jwt_info) == str: jwt_info = json.loads(jwt_info) network = jwt_info['network'] # Check whether the user is able to use this API by checking their rate limiter response = self.rate_limiter.check_rate_limit( settings.DEPLOY_ETH_CONTRACT_LIMIT, api_key, self.DeployEthContract.__name__) if response: status_message = f'Number of daily access limit exceeded {response["result"]["daily_limit"]}' logging.debug(f"{did} : {api_key} : {status_message}") return sidechain_eth_pb2.Response(output=json.dumps(response), status_message=status_message, status=False) if network == "mainnet": hive_api_url = config( 'MAIN_NET_HIVE_PORT') + settings.HIVE_API_ADD_FILE web3 = Web3( HTTPProvider(config('MAIN_NET_SIDECHAIN_ETH_RPC_PORT'), request_kwargs={'timeout': 60})) else: hive_api_url = config( 'PRIVATE_NET_HIVE_PORT') + settings.HIVE_API_ADD_FILE web3 = Web3( HTTPProvider(config('PRIVATE_NET_SIDECHAIN_ETH_RPC_PORT'), request_kwargs={'timeout': 60})) # reading the file content eth_account_address = jwt_info['eth_account_address'] eth_gas = jwt_info['eth_gas'] eth_private_key = jwt_info['eth_private_key'] contract_name = jwt_info['contract_name'] contract_source = jwt_info['contract_source'] # upload smart contract code to hive response = requests.get(hive_api_url, headers=self.headers, files={'file': contract_source}, timeout=REQUEST_TIMEOUT) data = json.loads(response.text) hive_hash = data['Hash'] if not response: status_message = 'Error: Smart contract code could not be uploaded' logging.debug(f"{did} : {api_key} : {status_message}") return sidechain_eth_pb2.Response(output="", status_message=status_message, status=False) try: # We need this since our eth sidechain is POA web3.middleware_onion.inject(geth_poa_middleware, layer=0) if not web3.isConnected(): status_message = 'Error: Could not connect to Eth Sidechain node' logging.debug(f"{did} : {api_key} : {status_message}") return sidechain_eth_pb2.Response( output="", status_message=status_message, status=False) compiled_sol = compile_standard({ "language": "Solidity", "sources": { contract_name: { "content": contract_source, }, }, "settings": { 'evmVersion': 'byzantium', "outputSelection": { "*": { "*": ["*"] } } } }) # get bytecode bytecode = compiled_sol['contracts'][contract_name][contract_name][ 'evm']['bytecode']['object'] transaction = { 'gas': eth_gas, 'gasPrice': web3.eth.gasPrice, 'nonce': web3.eth.getTransactionCount( web3.toChecksumAddress(eth_account_address)), 'data': '0x' + bytecode } signed_tx = web3.eth.account.sign_transaction( transaction, eth_private_key) if signed_tx is None: status_message = 'Error: Transaction could not be signed' logging.debug(f"{did} : {api_key} : {status_message}") return sidechain_eth_pb2.Response( output="", status_message=status_message, status=False) # Submit the transaction that deploys the contract tx_hash = web3.eth.sendRawTransaction(signed_tx.rawTransaction) if tx_hash is None: status_message = 'Error: Transaction could not be send to deploy contract' logging.debug(f"{did} : {api_key} : {status_message}") return sidechain_eth_pb2.Response( output="", status_message=status_message, status=False) # Wait for the transaction to be mined, and get the transaction receipt tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash) except Exception as e: status_message = 'Error Occurred while deploying the Ethereum Contract' logging.debug( f"DeployEthContract : {did} : {api_key} : {status_message} : {e}" ) return sidechain_eth_pb2.Response(output='', status_message=status_message, status=False) # generate jwt token jwt_info = { 'result': { 'contract_address': tx_receipt.contractAddress, 'contract_name': contract_name, 'contract_code_hash': hive_hash, } } jwt_token = jwt.encode( { 'jwt_info': jwt_info, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=settings.TOKEN_EXPIRATION) }, api_key, algorithm='HS256') return sidechain_eth_pb2.Response( output=jwt_token, status_message='Successfully deployed Eth Smart Contract', status=True)
def WatchEthContract(self, request, context): metadata = dict(context.invocation_metadata()) did = metadata["did"] api_key = get_api_from_did(did) try: jwt_info = jwt.decode(request.input, key=api_key, algorithms=['HS256']).get('jwt_info') except Exception as e: status_message = 'Authentication Error' logging.debug( f"WatchEthContract : {did} : {api_key} : {status_message} : {e}" ) return sidechain_eth_pb2.Response(output='', status_message=status_message, status=False) if type(jwt_info) == str: jwt_info = json.loads(jwt_info) network = jwt_info['network'] # Check whether the user is able to use this API by checking their rate limiter response = self.rate_limiter.check_rate_limit( settings.WATCH_ETH_CONTRACT_LIMIT, api_key, self.WatchEthContract.__name__) if response: status_message = f'Number of daily access limit exceeded {response["result"]["daily_limit"]}' logging.debug(f"{did} : {api_key} : {status_message}") return sidechain_eth_pb2.Response(output=json.dumps(response), status_message=status_message, status=False) if network == "mainnet": hive_api_url = config( 'MAIN_NET_HIVE_PORT') + settings.HIVE_API_RETRIEVE_FILE + "{}" web3 = Web3( HTTPProvider(config('MAIN_NET_SIDECHAIN_ETH_RPC_PORT'), request_kwargs={'timeout': 60})) else: hive_api_url = config('PRIVATE_NET_HIVE_PORT' ) + settings.HIVE_API_RETRIEVE_FILE + "{}" web3 = Web3( HTTPProvider(config('PRIVATE_NET_SIDECHAIN_ETH_RPC_PORT'), request_kwargs={'timeout': 60})) # reading the file content contract_address = jwt_info['contract_address'] contract_name = jwt_info['contract_name'] contract_code_hash = jwt_info['contract_code_hash'] # show smart contract code from Hive response = requests.get(hive_api_url.format(contract_code_hash), headers=self.headers, timeout=REQUEST_TIMEOUT) contract_source = response.text if not response: status_message = 'Error: Smart contract code could not be retrieved from Hive' logging.debug(f"{did} : {api_key} : {status_message}") return sidechain_eth_pb2.Response(output="", status_message=status_message, status=False) try: # Get smart contract details compiled_sol = compile_standard({ "language": "Solidity", "sources": { contract_name: { "content": contract_source, }, }, "settings": { 'evmVersion': 'byzantium', "outputSelection": { "*": { "*": ["*"] } } } }) abi = json.loads(compiled_sol['contracts'][contract_name] [contract_name]['metadata'])['output']['abi'] # We need this since our eth sidechain is POA web3.middleware_onion.inject(geth_poa_middleware, layer=0) contract = web3.eth.contract(address=contract_address, abi=abi) functions = contract.all_functions() contract_functions = [] for function in functions: contract_function = repr(function)[1:-1].split()[1] contract_functions.append(contract_function) except Exception as e: status_message = 'Error Occurred while watching the Ethereum Contract' logging.debug( f"WatchEthContract : {did} : {api_key} : {status_message} : {e}" ) return sidechain_eth_pb2.Response(output='', status_message=status_message, status=False) # generate jwt token jwt_info = { 'result': { 'contract_address': contract_address, 'contract_functions': contract_functions, 'contract_source': contract_source } } jwt_token = jwt.encode( { 'jwt_info': jwt_info, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=settings.TOKEN_EXPIRATION) }, api_key, algorithm='HS256') return sidechain_eth_pb2.Response( output=jwt_token, status_message='Successfully viewed Eth Smart Contract', status=True)