Skip to content

Commit

Permalink
docs: adding subroutines section; updating lsig section;
Browse files Browse the repository at this point in the history
  • Loading branch information
aorumbayev committed Aug 5, 2024
1 parent d4980bb commit e73c0b9
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 28 deletions.
1 change: 1 addition & 0 deletions docs/testing-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ transactions
contract-testing
signature-testing
state-management
subroutines
opcodes
```
10 changes: 0 additions & 10 deletions docs/testing-guide/opcodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@

The [coverage](coverage.md) file contains a complete list of all opcodes and respective types as well as whether they are _Mockable_, _Emulated_, or _Native_ within the `algorand-python-testing` package. The following section will highlight common opcodes and types that usually require interaction with test context manager. `Native` opcodes are assumed to function as they are in the Algorand Virtual Machine given that their functionality is stateless, if you experience any issues with any of the `Native` opcodes, please raise an issue in the [`algorand-python-testing` repo](https://github.com/algorandfoundation/algorand-python-testing/issues/new/choose) repository or contribute a PR by following [Contributing](https://github.com/algorandfoundation/algorand-python-testing/blob/main/CONTRIBUTING.md) guide.

## Subroutines

Any python function decorated with `@algopy.subroutine` is accessible as regular python function when accessed within the testing context. Which implies no additional setup or teardown is required, simply instantiate the class holding the function and access the function as a regular python instance attribute.

See `simple_voting` examples under [examples](../examples.md) for a showcase of testing subroutines.

```{hint}
Testing `subroutines` is a unique feature of `algorand-python-testing`, in contrast with integration tests against real AVM network, this approach allows validating critical logic of narrowly scoped business logic of the contract class without the need to access it via public method that relies on it. In a real AVM network, a user would have to deploy a contract, assemble and submit application call/group to the network, and await for the right results to be implcitly hit by the `subroutine`.
```

## Mockable types

Refer to [coverage](coverage.md) to see which opcodes are of type _Mockable_ - implying that they aren't either emulated or implemented by `algorand-python-testing` due to complexity or edge cases which require real AVM interaction.
71 changes: 53 additions & 18 deletions docs/testing-guide/signature-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,73 @@ Smart signatures, also known as logic signatures or LogicSigs, are programs that

## Defining a Logic Signature

To define a logic signature, you can use the `algopy.LogicSig` class:
To define a logic signature, you can use the `@logicsig` decorator:

```python
from algopy import LogicSig, Bytes, UInt64
from algopy import logicsig, Account, Txn, Global, UInt64, Bytes

def my_logic_sig() -> UInt64:
@logicsig
def hashed_time_locked_lsig() -> bool:
# Your logic signature code here
return UInt64(1) # Approve the transaction
return True # Approve the transaction
```

## Executing a Logic Signature

To test a logic signature, use the `execute_logicsig` method of the `AlgopyTestContext`:
To test a logic signature, use the `execute_logicsig` method of the `AlgopyTestContext`. You can provide arguments to the logic signature using the `scoped_lsig_args` context manager:

```python
from algopy import LogicSig, Bytes, UInt64
from algopy_testing import AlgopyTestContext

def my_logic_sig() -> UInt64:
# Your logic signature code here
return UInt64(1) # Approve the transaction
def test_logic_signature(context: AlgopyTestContext) -> None:
# Set up the transaction group
context.set_transaction_group(
[
context.any_payment_transaction(
# Transaction fields...
),
],
active_transaction_index=0,
)

# Execute the logic signature with arguments
with context.scoped_lsig_args([algopy.Bytes(b"secret")]):
result = context.execute_logicsig(hashed_time_locked_lsig)

assert result is True
```

The `execute_logicsig` method takes one parameter which is an `lsig` itself, an instance of the logic signature function decorated with `@logicsig`.

The method returns the result of executing the logic signature, which is a `bool`:

- If the logic signature returns `True`, it emulates approval of the transaction.
- If it returns `False`, it emulates rejection of the transaction.

# note, lsig_args represent a list of any Bytes
result = ctx.execute_logicsig(my_logic_sig, lsig_args=[])
## Using `scoped_lsig_args`

The [`scoped_lsig_args`](#algopy_testing.context.AlgopyTestContext.scoped_lsig_args) context manager allows you to provide arguments to the logic signature for the duration of its execution. This is particularly useful when your logic signature expects input parameters accessed via `algopy.op.arg(n)`.

```python
with context.scoped_lsig_args([algopy.Bytes(b"secret")]):
result = context.execute_logicsig(hashed_time_locked_lsig)
```

The `execute_logicsig` method takes two parameters:
In this example, `b"secret"` is passed as an argument to the logic signature. Inside the logic signature, you can access this argument using `algopy.op.arg(0)`.

1. `lsig`: An instance of `algopy.LogicSig` containing the logic signature program.
2. `lsig_args`: An optional sequence of `algopy.Bytes` objects representing the arguments to the logic signature.
## Accessing Arguments in the Logic Signature

The method returns the result of executing the logic signature, which can be either a `bool` or a `UInt64`:
Within your logic signature, you can access the provided arguments using the `algopy.op.arg()` function:

- If the logic signature returns `0`, it's interpreted as `False` (rejection).
- If it returns `1`, it's interpreted as `True` (approval).
- Any other non-zero value is returned as a `UInt64`.
```python
@logicsig
def hashed_time_locked_lsig() -> bool:
secret = algopy.op.arg(0)
# Use the secret in your logic
is_secret_correct = algopy.op.sha256(secret) == expected_hash
# ... rest of the logic
```

```{hint}
For coverage details on `algopy.op.arg` and other available operations, see the [Algorand Python Testing Framework Reference](../coverage.md).
```
9 changes: 9 additions & 0 deletions docs/testing-guide/subroutines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Subroutines

Any python function decorated with `@algopy.subroutine` is accessible as regular python function when accessed within the testing context. Which implies no additional setup or teardown is required, simply instantiate the class holding the function and access the function as a regular python instance attribute.

See `simple_voting` examples under [examples](../examples.md) for a showcase of testing subroutines.

```{hint}
Testing `subroutines` is a unique feature of `algorand-python-testing`, in contrast with integration tests against real AVM network, this approach allows validating critical logic of narrowly scoped business logic of the contract class without the need to access it via public method that relies on it. In a real AVM network, a user would have to deploy a contract, assemble and submit application call/group to the network, and await for the right results to be implcitly hit by the `subroutine`.
```

0 comments on commit e73c0b9

Please sign in to comment.