go-algorand
go-algorand copied to clipboard
Assert with reason code
I found the assertion opcode to be very useful to prematurely abort execution on failed checks, but how difficult would be to have an opcode like
assert i
where i is an integer representing reason code , that allows client code to identify where the program failed. I know there is a program counter information but that's very error prone for development purposes.
Thanks in advance.
I believe this problem was resolved with #3082, but let me know if not.
Hi @algoanne , it was not the same thing unfortunately.
What is needed from my point of view is to be able to trigger asserts from TEAL that can return a code to the caller, so automated tests for contracts can be built . With complex contracts, where multiple assertions maybe thrown, it is hard or nearly impossible to track execution. And using the "program counter" information for this purpose is not useful, since any modification would break down the entire test set.
thanks for the additional context!
Adding a comment here for extra support for this feature. I raised this issue in Discord and we had a discussion. No agreement reached but I still strongly believe something like this is required. Not just for testing/debugging but for end-user applications that need to display friendly/meaningful error messages depending on why the program failed. Raising an integer error code would make that possible and would be very developer friendly.
@fergalwalsh I talked with @jannotti about this also. I'm interested -in particular- in deep testing features but error reporting in general is much needed in Algorand. Logging/state use is not feasible since any non-elemental solution in Algorand involves stateless contracts. At our team we are expectant on the possibility of the future integration of assembly-to-line-map feature to be able to track execution (e.g: https://github.com/algorand/go-algorand/pull/3322)
Related to this, here's a little hack for assertions that gives the python line number that may be useful to you: https://github.com/certusone/wormhole/blob/cc72c2a644c65859eaae5103c925e6e95557e706/algorand/wormhole_core.py#L62 @hernandp @fergalwalsh
Summarizing a brainstormed idea during live discussion:
- Idea: Support referencing an assertion by associating an alias to each PyTeal source line containing an
assertat source map creation time. - How it works:
- In PyTeal, expose an API akin to
NamedAssert(condition: Expr, name: str).nameis an alias providing a convenient way to refer to a specific assertion.- It's possible
namecan take other data types.
- It's possible
- Create a PyTeal-to-symbolic name source map alongside the TEAL-to-PyTeal source map (via https://github.com/algorand/pyteal/issues/449).
- Compose source maps to make these jumps: PC -> TEAL -> PyTeal -> assert alias.
- Given the association with an assert alias, it seems plausible to allow a Python
assertcomparing returned assert alias to expected alias.
- In PyTeal, expose an API akin to
- Note - The approach does not modify TEAL source. So, it does not impact program size or evaluation cost.
I went with something similar in the end. Since I last commented on this issue we have begun using a new higher level language that compiles to Teal rather than writing Teal directly. In the HLL we do something like this:
assert(output_amount >= min_amount, "Output below expected amount")
which compiles to a normal assert but the compiler also tracks the location of the assert so it can produce a mapping of pc to error messages. We plan to include this mapping as errors.json within our contracts repo so sdks/clients can read this and return useful errors to the user.
This would still be very painful to do though for the poor souls still handwriting Teal.
I think the corresponding tool on Teal would take a label name in the unit test. You would add a label for the purpose of testing a particular assert.
txn Sender
addr HARD CODED
==
auth_check: assert
Your unit test would then want to be able to invoke the app and assert that the returned PC equals the auth_check label. That seems easy with the source maps.