From e0244764a938411a04bb8daeddcc278104e8b81a Mon Sep 17 00:00:00 2001 From: Hang Su Date: Fri, 19 Aug 2022 15:59:23 -0400 Subject: [PATCH 01/11] deprecate stuffs, prototype SanityCheckProgram --- algosdk/future/transaction.py | 4 +-- algosdk/logic.py | 62 +++++++++++++++++++++++++++++++++++ algosdk/testing/dryrun.py | 2 +- algosdk/transaction.py | 4 +-- 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/algosdk/future/transaction.py b/algosdk/future/transaction.py index 6c6da6e9..dbe3159d 100644 --- a/algosdk/future/transaction.py +++ b/algosdk/future/transaction.py @@ -2505,7 +2505,7 @@ class LogicSig: """ def __init__(self, program, args=None): - if not program or not logic.check_program(program, args): + if not program or not logic.sanity_check_program(program): raise error.InvalidProgram() self.logic = program self.args = args @@ -2548,7 +2548,7 @@ def verify(self, public_key): return False try: - logic.check_program(self.logic, self.args) + logic.sanity_check_program(self.logic) except error.InvalidProgram: return False diff --git a/algosdk/logic.py b/algosdk/logic.py index 72253d46..e8a1141e 100644 --- a/algosdk/logic.py +++ b/algosdk/logic.py @@ -1,4 +1,5 @@ import base64 +import binascii import json import os @@ -12,8 +13,31 @@ opcodes = None +def sanity_check_program(program): + if not program: + raise error.InvalidProgram("empty program") + + try: + if base64.encodebytes(base64.b64decode(str(program))) == str(program): + return False + except binascii.Error: + pass + + try: + encoding.decode_address(str(program)) + return False + except error.WrongChecksumError: + pass + except error.WrongKeyLengthError: + pass + + return True + + def check_program(program, args=None): """ + NOTE: This class is deprecated + Performs program checking for max length and cost Args: @@ -31,6 +55,9 @@ def check_program(program, args=None): def read_program(program, args=None): + """ + NOTE: This class is deprecated + """ global spec, opcodes intcblock_opcode = 32 bytecblock_opcode = 38 @@ -107,11 +134,17 @@ def read_program(program, args=None): def check_int_const_block(program, pc): + """ + NOTE: This class is deprecated + """ size, _ = read_int_const_block(program, pc) return size def read_int_const_block(program, pc): + """ + NOTE: This class is deprecated + """ size = 1 ints = [] num_ints, bytes_used = parse_uvarint(program[pc + size :]) @@ -134,11 +167,17 @@ def read_int_const_block(program, pc): def check_byte_const_block(program, pc): + """ + NOTE: This class is deprecated + """ size, _ = read_byte_const_block(program, pc) return size def read_byte_const_block(program, pc): + """ + NOTE: This class is deprecated + """ size = 1 bytearrays = [] num_ints, bytes_used = parse_uvarint(program[pc + size :]) @@ -164,11 +203,17 @@ def read_byte_const_block(program, pc): def check_push_int_block(program, pc): + """ + NOTE: This class is deprecated + """ size, _ = read_push_int_block(program, pc) return size def read_push_int_block(program, pc): + """ + NOTE: This class is deprecated + """ size = 1 single_int, bytes_used = parse_uvarint(program[pc + size :]) if bytes_used <= 0: @@ -180,11 +225,17 @@ def read_push_int_block(program, pc): def check_push_byte_block(program, pc): + """ + NOTE: This class is deprecated + """ size, _ = read_push_byte_block(program, pc) return size def read_push_byte_block(program, pc): + """ + NOTE: This class is deprecated + """ size = 1 item_len, bytes_used = parse_uvarint(program[pc + size :]) if bytes_used <= 0: @@ -200,6 +251,9 @@ def read_push_byte_block(program, pc): def parse_uvarint(buf): + """ + NOTE: This class is deprecated + """ x = 0 s = 0 for i, b in enumerate(buf): @@ -215,6 +269,8 @@ def parse_uvarint(buf): def address(program): """ + NOTE: This class is deprecated + Return the address of the program. Args: @@ -230,6 +286,8 @@ def address(program): def teal_sign(private_key, data, contract_addr): """ + NOTE: This class is deprecated + Return the signature suitable for ed25519verify TEAL opcode Args: @@ -254,6 +312,8 @@ def teal_sign(private_key, data, contract_addr): def teal_sign_from_program(private_key, data, program): """ + NOTE: This class is deprecated + Return the signature suitable for ed25519verify TEAL opcode Args: @@ -270,6 +330,8 @@ def teal_sign_from_program(private_key, data, program): def get_application_address(appID: int) -> str: """ + NOTE: This class is deprecated + Return the escrow address of an application. Args: diff --git a/algosdk/testing/dryrun.py b/algosdk/testing/dryrun.py index 0c54a1a6..bc561e16 100644 --- a/algosdk/testing/dryrun.py +++ b/algosdk/testing/dryrun.py @@ -511,7 +511,7 @@ def build_dryrun_request( ): """ Helper function for creation DryrunRequest object from a program. - By default it uses logic sig mode + By default, it uses logic sig mode and if app_idx / on_complete are set then application call is made Args: diff --git a/algosdk/transaction.py b/algosdk/transaction.py index 8c325b91..e7040bad 100644 --- a/algosdk/transaction.py +++ b/algosdk/transaction.py @@ -1318,7 +1318,7 @@ class LogicSig: """ def __init__(self, program, args=None): - if not program or not logic.check_program(program, args): + if not program or not logic.sanity_check_program(program): raise error.InvalidProgram() self.logic = program self.args = args @@ -1361,7 +1361,7 @@ def verify(self, public_key): return False try: - logic.check_program(self.logic, self.args) + logic.sanity_check_program(self.logic) except error.InvalidProgram: return False From e96fc814289b1d1d77ed33b017372875fcf0a5a8 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Tue, 23 Aug 2022 13:23:17 -0400 Subject: [PATCH 02/11] unit test --- .test-env | 3 ++- algosdk/future/transaction.py | 3 ++- algosdk/logic.py | 44 +++++++++++++++++++++-------------- algosdk/transaction.py | 3 ++- tests/steps/steps.py | 39 ++++++++++++++++++++++++++++++- tests/unit.tags | 3 ++- 6 files changed, 73 insertions(+), 22 deletions(-) diff --git a/.test-env b/.test-env index 3b17bb19..16d51885 100644 --- a/.test-env +++ b/.test-env @@ -1,6 +1,7 @@ # Configs for testing repo download: SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing" -SDK_TESTING_BRANCH="master" +# TODO revert back to "master" after sdk-testing is merged +SDK_TESTING_BRANCH="deprecate-langspec" SDK_TESTING_HARNESS="test-harness" VERBOSE_HARNESS=0 diff --git a/algosdk/future/transaction.py b/algosdk/future/transaction.py index dbe3159d..3ebedcf9 100644 --- a/algosdk/future/transaction.py +++ b/algosdk/future/transaction.py @@ -2505,8 +2505,9 @@ class LogicSig: """ def __init__(self, program, args=None): - if not program or not logic.sanity_check_program(program): + if not program: raise error.InvalidProgram() + logic.sanity_check_program(program) self.logic = program self.args = args self.sig = None diff --git a/algosdk/logic.py b/algosdk/logic.py index e8a1141e..b6f3269e 100644 --- a/algosdk/logic.py +++ b/algosdk/logic.py @@ -14,24 +14,34 @@ def sanity_check_program(program): - if not program: - raise error.InvalidProgram("empty program") + def is_ascii_printable(program_bytes): + return all( + map( + lambda x: x == ord("\n") or (ord(" ") <= x <= ord("~")), + program_bytes, + ) + ) + + if is_ascii_printable(program): + try: + encoding.decode_address(program.decode("utf-8")) + raise error.InvalidProgram( + "requesting program bytes, get Algorand address" + ) + except error.WrongChecksumError: + pass + except error.WrongKeyLengthError: + pass + + try: + base64.b64decode(program.decode("utf-8")) + raise error.InvalidProgram("program should not be b64 encoded") + except binascii.Error: + pass - try: - if base64.encodebytes(base64.b64decode(str(program))) == str(program): - return False - except binascii.Error: - pass - - try: - encoding.decode_address(str(program)) - return False - except error.WrongChecksumError: - pass - except error.WrongKeyLengthError: - pass - - return True + raise error.InvalidProgram( + "program bytes are all ASCII printable characters, not looking like Teal byte code" + ) def check_program(program, args=None): diff --git a/algosdk/transaction.py b/algosdk/transaction.py index e7040bad..65a2ce85 100644 --- a/algosdk/transaction.py +++ b/algosdk/transaction.py @@ -1318,8 +1318,9 @@ class LogicSig: """ def __init__(self, program, args=None): - if not program or not logic.sanity_check_program(program): + if not program: raise error.InvalidProgram() + logic.sanity_check_program(program) self.logic = program self.args = args self.sig = None diff --git a/tests/steps/steps.py b/tests/steps/steps.py index 2534836d..09d6e418 100644 --- a/tests/steps/steps.py +++ b/tests/steps/steps.py @@ -1,6 +1,7 @@ import base64 import random import time +import parse from algosdk import ( account, @@ -14,9 +15,18 @@ wallet, ) from algosdk.future import transaction -from behave import given, then, when +from behave import given, then, when, register_type from nacl.signing import SigningKey + +@parse.with_pattern(r".*") +def parse_string(text): + return text + + +register_type(MaybeString=parse_string) + + token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" algod_port = 60000 kmd_port = 60001 @@ -984,3 +994,30 @@ def buildTxn(t, sender, params): elif "nonparticipation" in t: txn = transaction.KeyregNonparticipatingTxn(sender, params) return txn + + +@given( + 'a base64 encoded program bytes for heuristic sanity check "{b64encoded}"' +) +def take_b64_encoded_bytes(context, b64encoded): + context.seemingly_program = base64.b64decode(b64encoded) + + +@when("I start heuristic sanity check over the bytes") +def heuristic_check_over_bytes(context): + context.sanity_check_err = "" + + try: + logic.sanity_check_program(context.seemingly_program) + except Exception as e: + print(e) + context.sanity_check_err = str(e) + + +@then('if there exists an error, the error contains "{err_msg:MaybeString}"') +def check_error_if_matching(context, err_msg: str = None): + print(err_msg) + if len(err_msg) > 0: + assert err_msg in context.sanity_check_err + else: + assert len(context.sanity_check_err) == 0 diff --git a/tests/unit.tags b/tests/unit.tags index 9e15ea1f..db883ba7 100644 --- a/tests/unit.tags +++ b/tests/unit.tags @@ -12,6 +12,7 @@ @unit.indexer.ledger_refactoring @unit.indexer.logs @unit.offline +@unit.program_sanity_check @unit.rekey @unit.responses @unit.responses.231 @@ -20,4 +21,4 @@ @unit.tealsign @unit.transactions @unit.transactions.keyreg -@unit.transactions.payment \ No newline at end of file +@unit.transactions.payment From ca4ed19e8ea12d651120d65f82e537b75fd860a6 Mon Sep 17 00:00:00 2001 From: Hang Su <87964331+ahangsu@users.noreply.github.com> Date: Wed, 24 Aug 2022 09:56:58 -0400 Subject: [PATCH 03/11] Update tests/steps/steps.py --- tests/steps/steps.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/steps/steps.py b/tests/steps/steps.py index 09d6e418..cdfb4bf9 100644 --- a/tests/steps/steps.py +++ b/tests/steps/steps.py @@ -1016,7 +1016,6 @@ def heuristic_check_over_bytes(context): @then('if there exists an error, the error contains "{err_msg:MaybeString}"') def check_error_if_matching(context, err_msg: str = None): - print(err_msg) if len(err_msg) > 0: assert err_msg in context.sanity_check_err else: From 0607aaf5a7d59e615122dc328817a71c683df12a Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 24 Aug 2022 10:28:08 -0400 Subject: [PATCH 04/11] deprecation reason --- algosdk/logic.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/algosdk/logic.py b/algosdk/logic.py index b6f3269e..2899b6f7 100644 --- a/algosdk/logic.py +++ b/algosdk/logic.py @@ -14,6 +14,14 @@ def sanity_check_program(program): + """ + Performs heuristic program validation: + check if passed in bytes are Algorand address, or they are B64 encoded, rather than Teal bytes + + Args: + program (bytes): compiled program + """ + def is_ascii_printable(program_bytes): return all( map( @@ -46,7 +54,12 @@ def is_ascii_printable(program_bytes): def check_program(program, args=None): """ - NOTE: This class is deprecated + NOTE: This class is deprecated: + `langspec.json` can no longer correctly to depicting the cost model (as of 2022.08.22), + also to minimize the work in updating SDKs per AVM release, + we are deprecating`langspec.json` across all SDKs. + The behavior of method `checkProgram` relies on `langspec.json`. + Thus, this method is being deprecated. Performs program checking for max length and cost @@ -66,8 +79,14 @@ def check_program(program, args=None): def read_program(program, args=None): """ - NOTE: This class is deprecated + NOTE: This class is deprecated: + `langspec.json` can no longer correctly to depicting the cost model (as of 2022.08.22), + also to minimize the work in updating SDKs per AVM release, + we are deprecating`langspec.json` across all SDKs. + The behavior of method `checkProgram` relies on `langspec.json`. + Thus, this method is being deprecated. """ + global spec, opcodes intcblock_opcode = 32 bytecblock_opcode = 38 From b18f6f74a038655608bdf5f832048dc692d8b4c2 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Wed, 24 Aug 2022 15:28:32 -0400 Subject: [PATCH 05/11] add empty program check --- algosdk/logic.py | 3 +++ tests/steps/steps.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/algosdk/logic.py b/algosdk/logic.py index 2899b6f7..7746940c 100644 --- a/algosdk/logic.py +++ b/algosdk/logic.py @@ -30,6 +30,9 @@ def is_ascii_printable(program_bytes): ) ) + if program is None or len(program) == 0: + raise error.InvalidProgram("empty program") + if is_ascii_printable(program): try: encoding.decode_address(program.decode("utf-8")) diff --git a/tests/steps/steps.py b/tests/steps/steps.py index cdfb4bf9..c416b9ec 100644 --- a/tests/steps/steps.py +++ b/tests/steps/steps.py @@ -997,7 +997,7 @@ def buildTxn(t, sender, params): @given( - 'a base64 encoded program bytes for heuristic sanity check "{b64encoded}"' + 'a base64 encoded program bytes for heuristic sanity check "{b64encoded:MaybeString}"' ) def take_b64_encoded_bytes(context, b64encoded): context.seemingly_program = base64.b64decode(b64encoded) From f78bf11a95aae28e7faa267a6d8c7c7f94e425a5 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 25 Aug 2022 10:30:36 -0400 Subject: [PATCH 06/11] deprecation msg modify --- algosdk/logic.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/algosdk/logic.py b/algosdk/logic.py index 7746940c..ff2331cf 100644 --- a/algosdk/logic.py +++ b/algosdk/logic.py @@ -58,11 +58,8 @@ def is_ascii_printable(program_bytes): def check_program(program, args=None): """ NOTE: This class is deprecated: - `langspec.json` can no longer correctly to depicting the cost model (as of 2022.08.22), - also to minimize the work in updating SDKs per AVM release, - we are deprecating`langspec.json` across all SDKs. - The behavior of method `checkProgram` relies on `langspec.json`. - Thus, this method is being deprecated. + Validation relies on metadata (`langspec.json`) that does not accurately represent opcode behavior across program versions. + The behavior of `check_program` relies on `langspec.json`. Thus, this method is being deprecated. Performs program checking for max length and cost @@ -83,11 +80,8 @@ def check_program(program, args=None): def read_program(program, args=None): """ NOTE: This class is deprecated: - `langspec.json` can no longer correctly to depicting the cost model (as of 2022.08.22), - also to minimize the work in updating SDKs per AVM release, - we are deprecating`langspec.json` across all SDKs. - The behavior of method `checkProgram` relies on `langspec.json`. - Thus, this method is being deprecated. + Validation relies on metadata (`langspec.json`) that does not accurately represent opcode behavior across program versions. + The behavior of `read_program` relies on `langspec.json`. Thus, this method is being deprecated. """ global spec, opcodes From 80c2d79792623517e779f8b2f899f6032b1f6288 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 25 Aug 2022 10:54:28 -0400 Subject: [PATCH 07/11] make sanity check prog protected --- algosdk/future/transaction.py | 6 ++---- algosdk/logic.py | 4 ++-- algosdk/transaction.py | 6 ++---- tests/steps/steps.py | 3 +-- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/algosdk/future/transaction.py b/algosdk/future/transaction.py index 3ebedcf9..6c9cdfe0 100644 --- a/algosdk/future/transaction.py +++ b/algosdk/future/transaction.py @@ -2505,9 +2505,7 @@ class LogicSig: """ def __init__(self, program, args=None): - if not program: - raise error.InvalidProgram() - logic.sanity_check_program(program) + logic._sanity_check_program(program) self.logic = program self.args = args self.sig = None @@ -2549,7 +2547,7 @@ def verify(self, public_key): return False try: - logic.sanity_check_program(self.logic) + logic._sanity_check_program(self.logic) except error.InvalidProgram: return False diff --git a/algosdk/logic.py b/algosdk/logic.py index ff2331cf..30a092e8 100644 --- a/algosdk/logic.py +++ b/algosdk/logic.py @@ -13,7 +13,7 @@ opcodes = None -def sanity_check_program(program): +def _sanity_check_program(program): """ Performs heuristic program validation: check if passed in bytes are Algorand address, or they are B64 encoded, rather than Teal bytes @@ -30,7 +30,7 @@ def is_ascii_printable(program_bytes): ) ) - if program is None or len(program) == 0: + if not program: raise error.InvalidProgram("empty program") if is_ascii_printable(program): diff --git a/algosdk/transaction.py b/algosdk/transaction.py index 65a2ce85..28fe0286 100644 --- a/algosdk/transaction.py +++ b/algosdk/transaction.py @@ -1318,9 +1318,7 @@ class LogicSig: """ def __init__(self, program, args=None): - if not program: - raise error.InvalidProgram() - logic.sanity_check_program(program) + logic._sanity_check_program(program) self.logic = program self.args = args self.sig = None @@ -1362,7 +1360,7 @@ def verify(self, public_key): return False try: - logic.sanity_check_program(self.logic) + logic._sanity_check_program(self.logic) except error.InvalidProgram: return False diff --git a/tests/steps/steps.py b/tests/steps/steps.py index c416b9ec..08c47e3c 100644 --- a/tests/steps/steps.py +++ b/tests/steps/steps.py @@ -1008,9 +1008,8 @@ def heuristic_check_over_bytes(context): context.sanity_check_err = "" try: - logic.sanity_check_program(context.seemingly_program) + transaction.LogicSig(context.seemingly_program) except Exception as e: - print(e) context.sanity_check_err = str(e) From bddf9bc9275046ab475fbf28ccef33078b5e19c6 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 25 Aug 2022 11:26:48 -0400 Subject: [PATCH 08/11] more specific error checking santence --- tests/steps/steps.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/steps/steps.py b/tests/steps/steps.py index 08c47e3c..54e01036 100644 --- a/tests/steps/steps.py +++ b/tests/steps/steps.py @@ -1013,7 +1013,9 @@ def heuristic_check_over_bytes(context): context.sanity_check_err = str(e) -@then('if there exists an error, the error contains "{err_msg:MaybeString}"') +@then( + 'if the heuristic sanity check throws an error, the error contains "{err_msg:MaybeString}"' +) def check_error_if_matching(context, err_msg: str = None): if len(err_msg) > 0: assert err_msg in context.sanity_check_err From 5e0df77ab21a19fb7aaf3132a7dd351e5a83576b Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 25 Aug 2022 11:54:59 -0400 Subject: [PATCH 09/11] deprecate warning in python --- algosdk/logic.py | 79 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/algosdk/logic.py b/algosdk/logic.py index 30a092e8..e7788f10 100644 --- a/algosdk/logic.py +++ b/algosdk/logic.py @@ -2,6 +2,7 @@ import binascii import json import os +import warnings from . import constants from . import error @@ -57,7 +58,7 @@ def is_ascii_printable(program_bytes): def check_program(program, args=None): """ - NOTE: This class is deprecated: + NOTE: This method is deprecated: Validation relies on metadata (`langspec.json`) that does not accurately represent opcode behavior across program versions. The behavior of `check_program` relies on `langspec.json`. Thus, this method is being deprecated. @@ -73,16 +74,28 @@ def check_program(program, args=None): Raises: InvalidProgram: on error """ + warnings.warn( + "`check_program` relies on metadata (`langspec.json`) that " + "does not accurately represent opcode behavior across program versions. " + "This method is being deprecated.", + DeprecationWarning, + ) ok, _, _ = read_program(program, args) return ok def read_program(program, args=None): """ - NOTE: This class is deprecated: + NOTE: This method is deprecated: Validation relies on metadata (`langspec.json`) that does not accurately represent opcode behavior across program versions. The behavior of `read_program` relies on `langspec.json`. Thus, this method is being deprecated. """ + warnings.warn( + "`read_program` relies on metadata (`langspec.json`) that " + "does not accurately represent opcode behavior across program versions. " + "This method is being deprecated.", + DeprecationWarning, + ) global spec, opcodes intcblock_opcode = 32 @@ -161,16 +174,24 @@ def read_program(program, args=None): def check_int_const_block(program, pc): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) size, _ = read_int_const_block(program, pc) return size def read_int_const_block(program, pc): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) size = 1 ints = [] num_ints, bytes_used = parse_uvarint(program[pc + size :]) @@ -194,16 +215,24 @@ def read_int_const_block(program, pc): def check_byte_const_block(program, pc): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) size, _ = read_byte_const_block(program, pc) return size def read_byte_const_block(program, pc): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) size = 1 bytearrays = [] num_ints, bytes_used = parse_uvarint(program[pc + size :]) @@ -230,16 +259,24 @@ def read_byte_const_block(program, pc): def check_push_int_block(program, pc): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) size, _ = read_push_int_block(program, pc) return size def read_push_int_block(program, pc): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) size = 1 single_int, bytes_used = parse_uvarint(program[pc + size :]) if bytes_used <= 0: @@ -252,16 +289,24 @@ def read_push_int_block(program, pc): def check_push_byte_block(program, pc): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) size, _ = read_push_byte_block(program, pc) return size def read_push_byte_block(program, pc): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) size = 1 item_len, bytes_used = parse_uvarint(program[pc + size :]) if bytes_used <= 0: @@ -278,8 +323,12 @@ def read_push_byte_block(program, pc): def parse_uvarint(buf): """ - NOTE: This class is deprecated + NOTE: This method is deprecated """ + warnings.warn( + "This method is being deprecated.", + DeprecationWarning, + ) x = 0 s = 0 for i, b in enumerate(buf): @@ -295,8 +344,6 @@ def parse_uvarint(buf): def address(program): """ - NOTE: This class is deprecated - Return the address of the program. Args: @@ -312,8 +359,6 @@ def address(program): def teal_sign(private_key, data, contract_addr): """ - NOTE: This class is deprecated - Return the signature suitable for ed25519verify TEAL opcode Args: @@ -338,8 +383,6 @@ def teal_sign(private_key, data, contract_addr): def teal_sign_from_program(private_key, data, program): """ - NOTE: This class is deprecated - Return the signature suitable for ed25519verify TEAL opcode Args: @@ -356,8 +399,6 @@ def teal_sign_from_program(private_key, data, program): def get_application_address(appID: int) -> str: """ - NOTE: This class is deprecated - Return the escrow address of an application. Args: From bd0a67f7a8da680111f1c06d2c4db978c883b034 Mon Sep 17 00:00:00 2001 From: Hang Su <87964331+ahangsu@users.noreply.github.com> Date: Thu, 25 Aug 2022 18:52:26 -0400 Subject: [PATCH 10/11] Update .test-env --- .test-env | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.test-env b/.test-env index 16d51885..3b17bb19 100644 --- a/.test-env +++ b/.test-env @@ -1,7 +1,6 @@ # Configs for testing repo download: SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing" -# TODO revert back to "master" after sdk-testing is merged -SDK_TESTING_BRANCH="deprecate-langspec" +SDK_TESTING_BRANCH="master" SDK_TESTING_HARNESS="test-harness" VERBOSE_HARNESS=0 From a1dcc787df99e26e43a9930cab66a1ed7846afd5 Mon Sep 17 00:00:00 2001 From: Hang Su Date: Thu, 25 Aug 2022 19:17:08 -0400 Subject: [PATCH 11/11] move sanity check in logicsig --- algosdk/future/transaction.py | 47 +++++++++++++++++++++++++++++++++-- algosdk/logic.py | 43 -------------------------------- algosdk/transaction.py | 47 +++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 47 deletions(-) diff --git a/algosdk/future/transaction.py b/algosdk/future/transaction.py index 6c9cdfe0..df99f69d 100644 --- a/algosdk/future/transaction.py +++ b/algosdk/future/transaction.py @@ -1,5 +1,6 @@ from typing import List, Union import base64 +import binascii from enum import IntEnum import msgpack from collections import OrderedDict @@ -2505,12 +2506,54 @@ class LogicSig: """ def __init__(self, program, args=None): - logic._sanity_check_program(program) + self._sanity_check_program(program) self.logic = program self.args = args self.sig = None self.msig = None + @staticmethod + def _sanity_check_program(program): + """ + Performs heuristic program validation: + check if passed in bytes are Algorand address, or they are B64 encoded, rather than Teal bytes + + Args: + program (bytes): compiled program + """ + + def is_ascii_printable(program_bytes): + return all( + map( + lambda x: x == ord("\n") or (ord(" ") <= x <= ord("~")), + program_bytes, + ) + ) + + if not program: + raise error.InvalidProgram("empty program") + + if is_ascii_printable(program): + try: + encoding.decode_address(program.decode("utf-8")) + raise error.InvalidProgram( + "requesting program bytes, get Algorand address" + ) + except error.WrongChecksumError: + pass + except error.WrongKeyLengthError: + pass + + try: + base64.b64decode(program.decode("utf-8")) + raise error.InvalidProgram("program should not be b64 encoded") + except binascii.Error: + pass + + raise error.InvalidProgram( + "program bytes are all ASCII printable characters, not looking like Teal byte code" + ) + def dictify(self): od = OrderedDict() if self.args: @@ -2547,7 +2590,7 @@ def verify(self, public_key): return False try: - logic._sanity_check_program(self.logic) + self._sanity_check_program(self.logic) except error.InvalidProgram: return False diff --git a/algosdk/logic.py b/algosdk/logic.py index e7788f10..05e254c2 100644 --- a/algosdk/logic.py +++ b/algosdk/logic.py @@ -1,5 +1,4 @@ import base64 -import binascii import json import os import warnings @@ -14,48 +13,6 @@ opcodes = None -def _sanity_check_program(program): - """ - Performs heuristic program validation: - check if passed in bytes are Algorand address, or they are B64 encoded, rather than Teal bytes - - Args: - program (bytes): compiled program - """ - - def is_ascii_printable(program_bytes): - return all( - map( - lambda x: x == ord("\n") or (ord(" ") <= x <= ord("~")), - program_bytes, - ) - ) - - if not program: - raise error.InvalidProgram("empty program") - - if is_ascii_printable(program): - try: - encoding.decode_address(program.decode("utf-8")) - raise error.InvalidProgram( - "requesting program bytes, get Algorand address" - ) - except error.WrongChecksumError: - pass - except error.WrongKeyLengthError: - pass - - try: - base64.b64decode(program.decode("utf-8")) - raise error.InvalidProgram("program should not be b64 encoded") - except binascii.Error: - pass - - raise error.InvalidProgram( - "program bytes are all ASCII printable characters, not looking like Teal byte code" - ) - - def check_program(program, args=None): """ NOTE: This method is deprecated: diff --git a/algosdk/transaction.py b/algosdk/transaction.py index 28fe0286..f6389a6d 100644 --- a/algosdk/transaction.py +++ b/algosdk/transaction.py @@ -1,5 +1,6 @@ import base64 import msgpack +import binascii from collections import OrderedDict from . import account from . import constants @@ -1318,12 +1319,54 @@ class LogicSig: """ def __init__(self, program, args=None): - logic._sanity_check_program(program) + self._sanity_check_program(program) self.logic = program self.args = args self.sig = None self.msig = None + @staticmethod + def _sanity_check_program(program): + """ + Performs heuristic program validation: + check if passed in bytes are Algorand address, or they are B64 encoded, rather than Teal bytes + + Args: + program (bytes): compiled program + """ + + def is_ascii_printable(program_bytes): + return all( + map( + lambda x: x == ord("\n") or (ord(" ") <= x <= ord("~")), + program_bytes, + ) + ) + + if not program: + raise error.InvalidProgram("empty program") + + if is_ascii_printable(program): + try: + encoding.decode_address(program.decode("utf-8")) + raise error.InvalidProgram( + "requesting program bytes, get Algorand address" + ) + except error.WrongChecksumError: + pass + except error.WrongKeyLengthError: + pass + + try: + base64.b64decode(program.decode("utf-8")) + raise error.InvalidProgram("program should not be b64 encoded") + except binascii.Error: + pass + + raise error.InvalidProgram( + "program bytes are all ASCII printable characters, not looking like Teal byte code" + ) + def dictify(self): od = OrderedDict() if self.args: @@ -1360,7 +1403,7 @@ def verify(self, public_key): return False try: - logic._sanity_check_program(self.logic) + self._sanity_check_program(self.logic) except error.InvalidProgram: return False