From b9a3d316ce1833fec3c5556307a2fd6bfc94fce0 Mon Sep 17 00:00:00 2001 From: Dmitriy Borisenko Date: Mon, 6 Dec 2021 23:29:32 +0300 Subject: [PATCH 1/5] #354 Check result for errors --- proxy/common_neon/solana_interactor.py | 17 +++++++++-------- proxy/common_neon/transaction_sender.py | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/proxy/common_neon/solana_interactor.py b/proxy/common_neon/solana_interactor.py index 660ba9b25..758eda5ee 100644 --- a/proxy/common_neon/solana_interactor.py +++ b/proxy/common_neon/solana_interactor.py @@ -186,18 +186,19 @@ def extract_measurements_from_receipt(receipt): def check_if_program_exceeded_instructions(err_result): - err_instruction = "Program failed to complete: exceeded maximum number of instructions allowed" - err_budget = "failed: Computational budget exceeded" - - if err_result['data']['logs'][-1].find(err_instruction) >= 0 or \ - err_result['data']['logs'][-2].find(err_instruction) >= 0 or \ - err_result['data']['logs'][-1].find(err_budget) >= 0: - return True + error_arr = get_from_dict(err_result, "err", "InstructionError") + if error_arr is not None and isinstance(error_arr, list): + error_type = error_arr[1] + if isinstance(error_type, str): + if error_type == 'ProgramFailedToComplete': + return True + if error_type == 'ComputationalBudgetExceeded': + return True return False def check_if_storage_is_empty_error(err_result): - error_arr = get_from_dict(err_result, "data", "err", "InstructionError") + error_arr = get_from_dict(err_result, "err", "InstructionError") if error_arr is not None and isinstance(error_arr, list): error_dict = error_arr[1] if isinstance(error_dict, dict) and 'Custom' in error_dict: diff --git a/proxy/common_neon/transaction_sender.py b/proxy/common_neon/transaction_sender.py index 820bfb6b3..807823aa2 100644 --- a/proxy/common_neon/transaction_sender.py +++ b/proxy/common_neon/transaction_sender.py @@ -21,6 +21,7 @@ from .neon_instruction import NeonInstruction from .solana_interactor import SolanaInteractor, check_if_continue_returned, \ check_if_program_exceeded_instructions, check_if_storage_is_empty_error +from .utils import get_from_dict from ..environment import EVM_LOADER_ID from ..plugin.eth_proto import Trx as EthTrx @@ -321,6 +322,12 @@ def call_signed_noniterative(self): call_txs_05.add(self.create_acc_trx) call_txs_05.add(self.instruction.make_noniterative_call_transaction(len(call_txs_05.instructions))) result = self.sender.send_measured_transaction(call_txs_05, self.eth_trx, 'CallFromRawEthereumTX') + + if get_from_dict(result, 'result', 'meta', 'err') is not None: + if check_if_program_exceeded_instructions(result['result']['meta']): + raise Exception("Program failed to complete") + raise Exception(json.dumps(result['result']['meta'])) + return result['result']['transaction']['signatures'][0] @@ -359,7 +366,9 @@ def create_accounts_for_trx(self): logger.debug(f"Create account for trx: {length}") precall_txs = Transaction() precall_txs.add(self.create_acc_trx) - self.sender.send_measured_transaction(precall_txs, self.eth_trx, 'CreateAccountsForTrx') + result = self.sender.send_measured_transaction(precall_txs, self.eth_trx, 'CreateAccountsForTrx') + if get_from_dict(result, 'result', 'meta', 'err', 'InstructionError') is not None: + raise Exception("Failed to create account for trx") def write_trx_to_holder_account(self): @@ -421,7 +430,7 @@ def call_continue_step(self): result = self.sender.send_measured_transaction(trx, self.eth_trx, 'ContinueV02') return result except SendTransactionError as err: - if check_if_program_exceeded_instructions(err.result): + if check_if_program_exceeded_instructions(err.result['data']): step_count = int(step_count * 90 / 100) else: raise @@ -447,9 +456,9 @@ def call_continue_bucked(self): receipts.append(self.sender.send_transaction_unconfirmed(trx)) except SendTransactionError as err: logger.error(f"Failed to call continue bucked, error: {err.result}") - if check_if_storage_is_empty_error(err.result): + if check_if_storage_is_empty_error(err.result['data']): pass - elif check_if_program_exceeded_instructions(err.result): + elif check_if_program_exceeded_instructions(err.result['data']): steps = int(steps * 90 / 100) else: raise From 61b840ea43670643b1efc5fe1549fe1425c69b92 Mon Sep 17 00:00:00 2001 From: Dmitriy Borisenko Date: Tue, 7 Dec 2021 13:06:26 +0300 Subject: [PATCH 2/5] #354 get_measurements error -> warning --- proxy/common_neon/solana_interactor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/common_neon/solana_interactor.py b/proxy/common_neon/solana_interactor.py index 758eda5ee..483051a8f 100644 --- a/proxy/common_neon/solana_interactor.py +++ b/proxy/common_neon/solana_interactor.py @@ -103,7 +103,7 @@ def get_measurements(self, result): measurements = self.extract_measurements_from_receipt(result) for m in measurements: logger.info(json.dumps(m)) except Exception as err: - logger.error("Can't get measurements %s"%err) + logger.warning("Can't get measurements %s"%err) logger.info("Failed result: %s"%json.dumps(result, indent=3)) From d129f920a4425141ce3b4fc64c98a16cddc6ebfe Mon Sep 17 00:00:00 2001 From: Dmitriy Borisenko Date: Tue, 7 Dec 2021 13:36:29 +0300 Subject: [PATCH 3/5] #354 check for errors in get measurements --- proxy/common_neon/solana_interactor.py | 46 ++++++++++++++++++++++--- proxy/common_neon/transaction_sender.py | 15 ++++---- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/proxy/common_neon/solana_interactor.py b/proxy/common_neon/solana_interactor.py index 483051a8f..6ad86fb74 100644 --- a/proxy/common_neon/solana_interactor.py +++ b/proxy/common_neon/solana_interactor.py @@ -103,7 +103,7 @@ def get_measurements(self, result): measurements = self.extract_measurements_from_receipt(result) for m in measurements: logger.info(json.dumps(m)) except Exception as err: - logger.warning("Can't get measurements %s"%err) + logger.error("Can't get measurements %s"%err) logger.info("Failed result: %s"%json.dumps(result, indent=3)) @@ -133,6 +133,11 @@ def collect_results(self, receipts, eth_trx=None, reason=None): @staticmethod def extract_measurements_from_receipt(receipt): + if check_for_errors(receipt): + logger.warning("Can't get measurements from receipt with error") + logger.info("Failed result: %s"%json.dumps(receipt, indent=3)) + return [] + log_messages = receipt['result']['meta']['logMessages'] transaction = receipt['result']['transaction'] accounts = transaction['message']['accountKeys'] @@ -185,8 +190,39 @@ def extract_measurements_from_receipt(receipt): return result -def check_if_program_exceeded_instructions(err_result): - error_arr = get_from_dict(err_result, "err", "InstructionError") +def get_error_definition_from_reciept(receipt): + err_from_reciept = get_from_dict(receipt, 'result', 'meta', 'err', 'InstructionError') + if err_from_reciept is not None: + return err_from_reciept + + err_from_reciept_result = get_from_dict(receipt, 'meta', 'err', 'InstructionError') + if err_from_reciept_result is not None: + return err_from_reciept_result + + err_from_reciept_result_meta = get_from_dict(receipt, 'err', 'InstructionError') + if err_from_reciept_result_meta is not None: + return err_from_reciept_result_meta + + err_from_send_trx_error = get_from_dict(receipt, 'data', 'err', 'InstructionError') + if err_from_send_trx_error is not None: + return err_from_send_trx_error + + err_from_send_trx_error_data = get_from_dict(receipt, 'err', 'InstructionError') + if err_from_send_trx_error_data is not None: + return err_from_send_trx_error_data + + return None + + + +def check_for_errors(receipt): + if get_error_definition_from_reciept(receipt) is not None: + return True + return False + + +def check_if_program_exceeded_instructions(receipt): + error_arr = get_error_definition_from_reciept(receipt) if error_arr is not None and isinstance(error_arr, list): error_type = error_arr[1] if isinstance(error_type, str): @@ -197,8 +233,8 @@ def check_if_program_exceeded_instructions(err_result): return False -def check_if_storage_is_empty_error(err_result): - error_arr = get_from_dict(err_result, "err", "InstructionError") +def check_if_storage_is_empty_error(receipt): + error_arr = get_error_definition_from_reciept(receipt) if error_arr is not None and isinstance(error_arr, list): error_dict = error_arr[1] if isinstance(error_dict, dict) and 'Custom' in error_dict: diff --git a/proxy/common_neon/transaction_sender.py b/proxy/common_neon/transaction_sender.py index 807823aa2..5b8365403 100644 --- a/proxy/common_neon/transaction_sender.py +++ b/proxy/common_neon/transaction_sender.py @@ -19,9 +19,8 @@ from .emulator_interactor import call_emulated from .layouts import ACCOUNT_INFO_LAYOUT from .neon_instruction import NeonInstruction -from .solana_interactor import SolanaInteractor, check_if_continue_returned, \ +from .solana_interactor import SolanaInteractor, check_if_continue_returned, check_for_errors,\ check_if_program_exceeded_instructions, check_if_storage_is_empty_error -from .utils import get_from_dict from ..environment import EVM_LOADER_ID from ..plugin.eth_proto import Trx as EthTrx @@ -323,8 +322,8 @@ def call_signed_noniterative(self): call_txs_05.add(self.instruction.make_noniterative_call_transaction(len(call_txs_05.instructions))) result = self.sender.send_measured_transaction(call_txs_05, self.eth_trx, 'CallFromRawEthereumTX') - if get_from_dict(result, 'result', 'meta', 'err') is not None: - if check_if_program_exceeded_instructions(result['result']['meta']): + if check_for_errors(result): + if check_if_program_exceeded_instructions(result): raise Exception("Program failed to complete") raise Exception(json.dumps(result['result']['meta'])) @@ -367,7 +366,7 @@ def create_accounts_for_trx(self): precall_txs = Transaction() precall_txs.add(self.create_acc_trx) result = self.sender.send_measured_transaction(precall_txs, self.eth_trx, 'CreateAccountsForTrx') - if get_from_dict(result, 'result', 'meta', 'err', 'InstructionError') is not None: + if check_for_errors(result): raise Exception("Failed to create account for trx") @@ -430,7 +429,7 @@ def call_continue_step(self): result = self.sender.send_measured_transaction(trx, self.eth_trx, 'ContinueV02') return result except SendTransactionError as err: - if check_if_program_exceeded_instructions(err.result['data']): + if check_if_program_exceeded_instructions(err.result): step_count = int(step_count * 90 / 100) else: raise @@ -456,9 +455,9 @@ def call_continue_bucked(self): receipts.append(self.sender.send_transaction_unconfirmed(trx)) except SendTransactionError as err: logger.error(f"Failed to call continue bucked, error: {err.result}") - if check_if_storage_is_empty_error(err.result['data']): + if check_if_storage_is_empty_error(err.result): pass - elif check_if_program_exceeded_instructions(err.result['data']): + elif check_if_program_exceeded_instructions(err.result): steps = int(steps * 90 / 100) else: raise From d5d4f74d4807672be40a8499983bc2ad0eb3aad3 Mon Sep 17 00:00:00 2001 From: Dmitriy Borisenko Date: Tue, 7 Dec 2021 13:46:03 +0300 Subject: [PATCH 4/5] #354 get measurements from bucked transactions --- proxy/common_neon/transaction_sender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/common_neon/transaction_sender.py b/proxy/common_neon/transaction_sender.py index 5b8365403..3150ed885 100644 --- a/proxy/common_neon/transaction_sender.py +++ b/proxy/common_neon/transaction_sender.py @@ -501,7 +501,7 @@ def collect_bucked_results(self, receipts, reason): logger.debug(f"Collected bucked results: {receipts}") result_list = self.sender.collect_results(receipts, eth_trx=self.eth_trx, reason=reason) for result in result_list: - # self.sender.get_measurements(result) + self.sender.get_measurements(result) signature = check_if_continue_returned(result) if signature: return signature From 118458f27a7912f4a3aeddfc43a2e0b0a0c82015 Mon Sep 17 00:00:00 2001 From: Dmitriy Borisenko Date: Tue, 7 Dec 2021 14:40:08 +0300 Subject: [PATCH 5/5] #354 remove repeated code --- proxy/common_neon/solana_interactor.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/proxy/common_neon/solana_interactor.py b/proxy/common_neon/solana_interactor.py index 6ad86fb74..56015e127 100644 --- a/proxy/common_neon/solana_interactor.py +++ b/proxy/common_neon/solana_interactor.py @@ -199,17 +199,13 @@ def get_error_definition_from_reciept(receipt): if err_from_reciept_result is not None: return err_from_reciept_result - err_from_reciept_result_meta = get_from_dict(receipt, 'err', 'InstructionError') - if err_from_reciept_result_meta is not None: - return err_from_reciept_result_meta - err_from_send_trx_error = get_from_dict(receipt, 'data', 'err', 'InstructionError') if err_from_send_trx_error is not None: return err_from_send_trx_error - err_from_send_trx_error_data = get_from_dict(receipt, 'err', 'InstructionError') - if err_from_send_trx_error_data is not None: - return err_from_send_trx_error_data + err_from_prepared_receipt = get_from_dict(receipt, 'err', 'InstructionError') + if err_from_prepared_receipt is not None: + return err_from_prepared_receipt return None