Coder Social home page Coder Social logo

algorand / pyteal Goto Github PK

View Code? Open in Web Editor NEW
283.0 18.0 119.0 2.93 MB

Algorand Smart Contracts in Python

Home Page: https://pyteal.readthedocs.io

License: MIT License

Python 99.82% Makefile 0.18%
smart-contracts blockchain algorand cryptocurrency python-language-binding python avm teal

pyteal's Introduction

PyTeal logo

PyTeal: Algorand Smart Contracts in Python

Build Status PyPI version Documentation Status Code style: black

PyTeal is a Python language binding for Algorand Smart Contracts (ASC1s).

Algorand Smart Contracts are implemented using a new language that is stack-based, called Transaction Execution Approval Language (TEAL).

However, TEAL is essentially an assembly language. With PyTeal, developers can express smart contract logic purely using Python. PyTeal provides high level, functional programming style abstractions over TEAL and does type checking at construction time.

Install

PyTeal requires Python version >= 3.10.

If your operating system (OS) Python version < 3.10, we recommend:

  • Rather than override the OS Python version, install Python >= 3.10 alongside the OS Python version.
  • Use pyenv or similar tooling to manage multiple Python versions.

Recommended: Install from PyPi

Install the latest official release from PyPi:

  • pip install pyteal

Install Latest Commit

If needed, it's possible to install directly from the latest commit on master to use unreleased features:

WARNING: Unreleased code is experimental and may not be backwards compatible or function properly. Use extreme caution when installing PyTeal this way.

  • pip install git+https://github.com/algorand/pyteal

Documentation

Development Setup

Setup venv (one time):

  • python3 -m venv venv

Active venv:

  • . venv/bin/activate (if your shell is bash/zsh)
  • . venv/bin/activate.fish (if your shell is fish)

Pip install PyTeal in editable state with dependencies:

  • make setup-development
  • OR if you don't have make installed:
    • pip install -e . && pip install -r requirements.txt

Format code:

  • black .

Lint using flake8:

  • flake8 docs examples pyteal scripts tests *.py

Type checking using mypy:

  • mypy pyteal scripts

Run unit tests:

  • pytest pyteal tests/unit

Run integration tests (assumes a developer-mode algod is available on port 4001):

  • pytest tests/integration

Stand up developer-mode algod on ports 4001, 4002 and tealdbg on port 9392 (assumes Docker is available on your system):

  • docker-compose up -d

Tear down and clean up resources for the developer-mode algod stood up above:

  • docker-compose down

pyteal's People

Contributors

ahangsu avatar algochoi avatar algoidurovic avatar algojack avatar barnjamin avatar bbroder-algo avatar ciottigiorgio avatar eltociear avatar fabrice102 avatar hashmapsdata2value avatar hernandp avatar ipaleka avatar jasonpaulos avatar jdtzmn avatar joe-p avatar krotkiewicz avatar lvorithm avatar michaeldiamant avatar moataz-e avatar onetechnical avatar pablolion avatar shaih avatar shiqizng avatar shyba avatar stechu avatar stylishtriangles avatar thadguidry avatar tzaffi avatar ursify avatar winder avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyteal's Issues

Unittests?

I've already asked a similar question on the algorand developer forum here: https://forum.algorand.org/t/test-contracts-

I’ve lately started learning to develop smart contracts with TEAL on the algorand blockchain with pyteal and i really like it but i have noticed that the workflow for testing (especially unittesting) is very inconvenient. Publishing an app every time on the test net is very time-consuming and running a private network was also very difficult and cumbersome.

Wouldn't it be nice to have the possibility of testing inside the pyteal framework which allows us simultaneously mocking things like the arguments, global, local state, addresses and co. Obviously this would not replace testing on the testnet but would be a lot more convenient during development since it would be possible to also write test for partial code

If this is for some reason a stupid/unuseful idea you can obviously delete this issue, but would be nice to here your opinion about this idea.

If something like this is of interest for pyteal i would offer my help for development (since i'm new to the algorand ecosystem help would be appreciated ;) )

TEAL 3 support

Summary

TEAL version 3 is going to be released soon, so PyTeal should be updated to support the new opcodes and fields.

Scope

Add support for the following opcodes (with some possible exclusions):

  • getbit/setbit
  • getbyte/setbyte
  • gtxns/gtxnsa (for using a stack value as the txn index)
  • assert

New opcodes that can be potentially used for optimization:

  • pushint/pushbyte
  • swap
  • dig
  • select

Add support for the following transaction fields:

  • Assets/NumAssets
  • Applications/NumApplications
  • GlobalNumUint/GlobalNumByteSlice/LocalNumUint/LocalNumByteSlice

Add support for the following global field:

  • CreatorAddress

Urgency

Best to do soon so that users can take full advantage of TEAL 3.

Allow programs to use scratch space

Hey PyTEAL maintainers,
first of all thank you for this amazing library.

Can ask to add ScratchSlot examples to the documentation? Or at least here as a comment so I will be able to prepare a PR with documentation update. Thank you!

Simplify recursive subroutine calling in TEAL 5

Summary

TEAL 5 introduces the cover and uncover opcodes for stack manipulation, which can be used to greatly reduce the amount of opcodes and stack space needed to implement recursive subroutine calling.

Scope

Instead of using dig and pop to help spill scratch slots during a potentially recursive subroutine call in spillLocalSlotsDuringRecursion, cover and uncover can do a better job. There are comments in this section of code describing the exact improvements that can be made:

# TODO: TEAL 5+, do this instead:
# before.append(TealOp(None, Op.uncover, len(slots)))
# or just do cover during the previous loop where slots are loaded, whichever
# is more efficient
before.append(
TealOp(
None,
Op.dig,
len(slots) + subroutine.argumentCount() - 1,
)
)
# because we are stuck using dig instead of uncover in TEAL 4, we'll need to
# pop all of the dug up arguments after the function returns
preserveReturnValue = False
if subroutine.returnType != TealType.none:
# if the subroutine returns a value on the stack, we need to preserve this after
# restoring all local slots.
preserveReturnValue = True
if len(slots) > 1:
# Store the return value into slots[0] temporarily. As an optimization, if
# len(slots) == 1 we can just do a single swap instead
after.append(TealOp(None, Op.store, slots[0]))
# TODO: TEAL 5+, just do cover len(slots), so after restoring all slots the
# return value is on top of the stack
for slot in slots[::-1]:
# restore slots, iterating in reverse because slots[-1] is at the top of the stack
if preserveReturnValue and slot is slots[0]:
# time to restore the return value to the top of the stack
if len(slots) > 1:
# slots[0] is being used to store the return value, so load it again
after.append(TealOp(None, Op.load, slot))
# swap the return value with the actual value of slot[0] on the stack
after.append(TealOp(None, Op.swap))
after.append(TealOp(None, Op.store, slot))
for _ in range(numArgs):
# clear out the duplicate arguments that were dug up previously, since dig
# does not pop the dug values -- once we use cover/uncover to properly set up
# the spilled slots, this will no longer be necessary
if subroutine.returnType != TealType.none:
# if there is a return value on top of the stack, we need to preserve
# it, so swap it with the subroutine argument that's below it on the
# stack
after.append(TealOp(None, Op.swap))
after.append(TealOp(None, Op.pop))

Currently

Priority

Not strictly needed, but this is a worthwhile improvement for subroutines.

Optimize constants

Summary

PyTeal currently outputs code using the pseudo-ops int, byte, and addr. It's up to the assembler to transform these pseudo-ops into int and byte constant blocks. This process can be optimized to reduce program size if the constant blocks are sorted by decreasing frequency of use, and if the new pushint and pushbytes ops are used for single-use constants.

Scope

  • Modify the prototype implementation of this from #41 into a general feature.
  • Add an option to compileTeal to enable this feature. By default it should not be enabled to maintain compatibility, at least for now.

Urgency

This has the potential to significantly reduce cost program size for large programs.

Allow direct account/asset/app references

Summary

Starting in TEAL v4, opcode which accepted an index into one of the Accounts/ForeignAssets/ForeignApps arrays can now accept "direct references" to these objects as well. The PR containing this change: algorand/go-algorand#2264

PyTeal needs to update its type checking and documentation to allow this.

Scope

  • Update type checks & documentation for all affected expressions
  • Add additional unit tests to ensure expressions accept direct references without error

Urgency

This is required for full TEAL 4 support.

Cannot contact Algorand node.

I am working on the Teal Badge, and I created a Repository for my work. I am running the following from the command line in my node directory.

goal clerk compile stateless_swap.teal

The code is returning the following error.

Cannot contact Algorand node: open /Users/brianhaney/node/data/algod.net: no such file or directory

I had tried to run this in the Sandbox, as a solution to the Issue. However, developing in Sandbox led me to many more problems because the command line syntax, as well as the general directory were too convoluted. So, now I am focusing on solving this problem specifically for a node. I'd appreciate any suggestions or advice - thanks!

Document AssetHolding & AssetParam

Summary

Currently the examples and documentation don't show how to use AssetHolding and AssetParam.

Scope

Add a section to the documentation that shows how to properly use these expressions.

Urgency

This is a nice to have improvement.

Support byteslice math

Summary

TEAL v4 adds support for doing math on byteslices, meaning up to 512-bit uint math is now possible. PyTeal should support this as well.

Scope

Supporting this properly may take 2 steps:

  1. Add basic support for byteslice math by directly exposing functions like BytesAdd(a, b) to do math on byteslices.
  2. Create a more robust type system for PyTeal. This could mean new uint types (128-bit, 256-bit, etc.) that would compile to byteslices and use byteslice math if needed.

This issue is for just the first step.

Further optimize assembled constants

Summary

Currently the compiler has the option to assemble constants in an efficient way. This is done by ordering constants from most to least frequently referenced in the intc/bytecblock, and with pushint/pushbytes instead used for all constants that are referenced only once.

There is an additional optimization we can make for small integers.

Scope

If the integer can be encoded in 8 bits or less (TODO: check if this means a max of 255 or a max of 127, since ints are varuint encoded) and the integer is not one of the first 4 in intcblock, then it's always more efficient to use pushint to load the value instead of placing it in the constant block, regardless of how often its referenced. This is because the op to load from a the constant block, intc X, is 2 bytes, which is equivalent to using pushint for a sufficiently small integer.

If the integer can be encoded in 8 bits or less, but it is one of the first 4 in intcblock, then it is sometimes more efficient for the whole program to do this (only because it frees up one of the preferred spots at the beginning of the constant block, and the benefit to doing this may outweigh the loss of using pushint for a frequently-referenced integer). This is a more complicated case to analyze.

Compilation check if MaybeValue was evaluated before accessing it's value

Problem

Using a MaybeValue.value() (for ex. from AssetHolding.balance()) without evaluating it first causes a very unintuitive error message.
Currently:

pyteal.TealInternalError: Encountered 1 error during compilation

Investigating the value of errors in compileTeal:

[TealCompileError('Scratch slot load occurs before store', <pyteal.ScratchLoad object at 0x7f979628faf0>)]

The desired behaviour would be to raise an exception that mentions a MaybeValue used without evaluating it first (or remove slot dependency).

Solution

With TEAL 4 it's possible to use select opcode to choose the desired parameter (ok, value) and avoid slot usage inside MaybeValue completely.

Dependencies

None

Urgency

Low, mostly for convenience and simpler debugging

Document bit/byte manipulation ops from TEAL 3

Summary

TEAL 3 introduced new operations for manipulating bits and bytes and they should be documented.

Scope

Document the following expressions and show usage examples:

  • GetBit
  • SetBit
  • GetByte
  • SetByte

Urgency

Best to do before releasing v0.7.0.

Fix incorrect use of Txn.accounts[0]

Summary

Some parts of the examples and documentation are wrong about how the Txn.accounts array works. They treat Txn.accounts[0] as the first element of the txn's ForeignAccounts, but actually Txn.accounts[1] is the first element and 0 is the sender.

Scope

Update all examples and documentation to use Txn.accounts properly. Also add a section in the documentation that explicitly explains how Txn.accounts works.

Urgency

Should fix ASAP so developers do not get confused.

Identity element for logic operations

Problem

Right now if I write Int(1) as argument of And it gets actually compiled to the code. It would be nice if the compiler could calculate that an identity element doesn't change the result of the operation.
For example if I want to insert a check depending on the value of some parameter I actually have to change the whole expression.

return core_checks if value == 0 else And(core_checks, other_checks)

should become

return And(core_checks, identity_element if value == 0 else other_checks)

Solution

The compiler should detect identity elements for each operator and simplify them.

Urgency

No urgency at all.

localGetEx doesn't compile to TEAL properly

When using App.localGetEx(Int(1), Global.current_application_id(), Bytes("T1")) it pops out TEAL similar to load 1

However, the correct and expected output, which comes from using App.localGet(Int(1), Bytes("T1")) is

byte "T1"
app_local_get

Add compiler check for scratch space expressions

Summary

The compiler should throw an error if a scratch slot is loaded before any value is stored in it. While this is technically ok since a default value of 0 will be returned, it probably indicates a programming error. This would prevent users from creating ScratchVar and MaybeValue but forgetting to populate them.

Scope

This would involve some static analysis during compilation, most likely after the AST gets turned into a graph of TealBlocks.

Urgency

This should be done before the next release so users can use ScratchVar safely.

Add looping

Summary

TEAL v4 introduces the ability to jump backwards, meaning loops are now possible. PyTeal should have this functionality as well.

Scope

This is a hard problem to solve with PyTeal's concept of variables. There is essentially no mutation, it's all copy-on-write, which makes the semantics of a while/for loop hard to implement, since you have a constantly-changing tracking variable and you probably want to update variables inside the loop.

A solution is to use scratch slots exclusively for loop variables/conditions, since they can actually be mutated. This could look like:

i = ScratchVar()

# while loop with inline body
Seq([
    i.store(Int(0)),
    While(i.load() < Int(10))
        .Do(Seq([
            App.globalPut(Itob(i.load()), i.load() * Int(2)),
            i.store(i.load() + Int(1))
            # Continue()/Break() should work here too
        ]))
])

# for loop with inline body
For(i.store(Int(0)), i.load() < Int(10), i.store(i.load() + Int(1)))
    .Do(Seq([
        App.globalPut(Itob(i.load()), i.load() * Int(2)),
        i.store(i.load() + Int(1))
        # Continue()/Break() should work here too
    ]))

@Subroutine # see #71
def myLoopBody(i: Expr) -> Expr:
    # Continue()/Break() should NOT work here, since this crosses a subroutine boundary
    return App.globalPut(Itob(i.load()), i.load() * Int(2))

# while loop with subroutine body
Seq([
    i.store(Int(0)),
    While(i.load() < Int(10))
        .Do(Seq([
            myLoopBody(i),
            i.store(i.load() + Int(1))
        ])
])

# for loop with subroutine body
For(i.store(Int(0)), i.load() < Int(10), i.store(i.load() + Int(1)))
    .Do(myLoopBody(i))

# this INVALID behavior will not be possible because Python interprets i = Int(0) as a keyword arg!
For(i = Int(0), i < Int(10), i += Int(1))
    .Do(myLoopBody(i))

Ideally we'll want to aggressively optimize the use of scratch vars now to potentially pull them
onto the stack, since doing a simple loop involves a lot of scratch loads/stores.

Support importing/exporting values between contracts

Summary

TEAL v4 introduces the gload(s) ops, which can load values from the scratch space of prior app call transactions in the same group. PyTeal should support this feature as well.

Scope

edit: commenting out the initial idea in favor of a simpler approach inspired by @StylishTriangles's suggestion.

This issue requires the following changes:

  1. Allow ScratchSlot to have an additional optional parameter requestedSlotId, which would be an integer from 0-255 which specifies the slot ID that compiler must assign to this slot. This would also require modifications to compileTeal to actually assign the correct ID to any ScratchSlots with this parameter, and if multiple unique ScratchSlot request the same slot ID, compilation must fail.
  2. Also add an optional requestedSlotId parameter to ScratchVar which would be passed directly to its underlying ScratchSlot.
  3. Expose the gload op in a PyTeal expression (maybe as ImportScratchValue(txnIndex: Expr, slotId: int))

Ideally we can have a system where one contract can export many values (perhaps with helpful names that don't make it onto the chain) with something like Export("bidAmount", computedBidAmount). Then, another contract can import this exported value with something like computedBidAmount = ImportFrom(txnIndex, contract1.exports("bidAmount")).

This would require programs to be compiled in a specific order to make dependencies work, or all at once and the compiler can sort out dependencies.

Additionally, the compile method would need to return more information, perhaps a Python object that contains a dict of all exports from the contract and their assigned slots -- it's important to make this information accessible by the user so that they can properly debug, and it also lets other non-PyTeal contracts import their exports. This object then needs to be obtained by the importing contract in order to know which slots to actually load. Again, here it will be important to allow imports from non-PyTeal contracts (meaning essentially direct access to gload(s)).

Get current contract address

Is there a way to get the current address of the contract from pyteal? If there's not I think it would be a pretty useful feature to have

Basic optimization of generated code

Summary:

Code generator is pretty basic, we can do some pretty simple cleanup/optimization that makes it easier to use.

Scope

Urgency

Lots of people have been using it (lots of questions coming in) so trying to prioritize this

Add new syntax for `If` statements

Summary

Support an additional syntax for creating If statements. This new syntax would make complex statements easier to read.

Importantly, PyTeal should support this new syntax in addition to the current If syntax for backwards compatibility.

Scope

Consider the following pseudo-code which specifies an if statement:

if (i == 0) {
   j = 1;
} else if (i == 1) {
  j = 0;
} else {
  j = 2;
}

Current syntax

Currently, the way to encode this statement into PyTeal would be:

If(i == Int(0),
    Int(1),
    If(i == Int(1),
        Int(0),
        Int(2)
    )
)

This has the following disadvantages:

  • The positive and negative cases for each if statement are right next to each other. This is a little confusing because only 1 will get executed.
  • The indentation increases for every additional case.

New syntax

Instead, I propose the following new syntax:

If(i == Int(0))
    .Then(Int(1))
    .ElseIf(i == Int(1))
    .Then(Int(0))
    .Else(Int(2))

This syntax solves the above problems.

Lack of feature coverage table

Problem

Since PyTeal does not update in sync with Algorand releases teal code and version therefore a coverage table can help every dev to immediately understand which functionalities of teal is supported and which ones are not.

Solution

Creating, updating and maintaining a TEAL coverage (Or a not yet covered table if that's the case) table in README.md

Urgency

Medium

Add basic TEAL 4 opcodes to PyTeal

Summary

Add basic opcodes (as opposed to complex ones) that were added in TEAL 4. The divmodw and expw opcodes are not included because they will require additional design work.

Scope/Requirements

Implement and write tests for:

  • gload/gloads This needs some design work. Ideally you'd be able to choose specific values to "export" from one contract and be able to "import" them from another one, all without having to choose specific slot IDs. That's one way to do this at least.
  • gaid/gaids
  • ExtraProgramPages transaction field
  • Int-based math ops:
    • shl
    • shr
    • sqrt
    • bitlen
    • exp

Urgency/Relative Priority

Required to support TEAL 4 features.

Allow assignment to constant blocks

For a project I'm working on I'd like to know the exact byte location of given variables in the assembled teal for some static analysis.

Currently the intc/bytec blocks are created in the compilation processes and the variables are deduplicated which prevents the analysis I'd like to do.

Being able to call something like IntConst(0, Int(500)) or ByteConst(0, Byte("DEADBEEF")) that create the intc/bytecblock lines would allow me to define the constants I'm interested in and know their precise location. The ByteConst would have to convert to binary.

I think this will introduce another issue though because if those lines exist, the assembler will skip adding any other constants thinking that step has already been done.

The open PR here #41 appears to make some changes to the bits that would be relevant so I haven't dug into the pyteal lib yet to see how I might go about doing this.

Dynamic Array Reference

Problem

I'd like to be able to reference the elements of the Txn.application_args array dynamically using the looping construct with ScratchVars. Currently this is not supported but I see support is available in the Gtxn array.

example of what I'd like to do:

def set_properties(acct, args, start):
    idx = ScratchVar()

    init = idx.store(Int(start))
    cond = idx.load()<args.length()
    step = idx.store(idx.load() + Int(2))

    return Seq([
        For(init, cond, step).Do(
            App.localPut(acct, args[idx.load()], args[idx.load() + Int(1)])
        ),
        Int(1)
    ])

This may be supported in some way but I can't seem to find it.

Solution

Accept the Union[int,Expr] in the Txn array items __getitem__ call

Add subroutines

Summary

TEAL v4 introduces the callsub and retsub ops, which can be used to create subroutines in TEAL programs. PyTeal should have this functionality as well.

Scope

Introduce a @Subroutine function decoration. This decoration would modify the function and it might look like:

def Subroutine(targetFunc):
    subroutineID = # get a unique ID for this subroutine
    
    # save a reference to targetFunc somehow so the compiler can execute it later
    globalVarAllSubroutines[subroutineID] = targetFunc

    def subroutineCall(*args):
        # don't actually execute targetFunc, instead return a PyTeal expression indicating that you want to call it with args
        return CallSubroutine(subroutineID, args)

    return subroutineCall

Then during compilation, the compiler will collect all referenced subroutines, call the underlying functions to get their ASTs, and add them to the program's AST.

@Subroutine
def isEven(i: Expr) -> Expr:
    return If(i == Int(0),
        Int(1),
        If(i == Int(1),
            Int(0),
            isEven(i - Int(2))
        )
    )

    # using proposed If.Then.Else.ElseIf syntax (see #70)
    return If(i == Int(0))
        .Then(Int(1))
        .ElseIf(i == Int(1))
        .Then(Int(0))
        .Else(isEven(i - Int(2)))

Arguments

During compilation, arguments for subroutines can probably just be placed directly on the stack, like normal opcodes. The return value, if any, can also be placed on the stack for the caller to inspect.

Caller vs Callee Scratch Slots

In order to prevent subroutines from overriding scratch space values set by their calling code (the caller), PyTeal needs a notion of which scratch slots are caller owned and callee owned. Whenever a caller wishes to call a subroutine, it needs to move all values it cares about out of the callee-owned slots (either into caller-owned ones or spill onto the stack). Similarly, whenever a subroutine wishes to store something in a caller-owned slot, it needs to remember the value of that slot and restore it before the subroutine finishes (again, probably by spilling onto the stack).

New idea: don't have an explicit notation of caller- vs callee-owned slots; instead, identify all the slots each subroutine (and the main program) uses, then divide up the available slots according to this mapping. Spilling some slots to the stack would probably be necessary if not enough slots are available for every subroutine to have unique ones (and in the case of recursion), but this scheme would minimize slot storing/loading.

Recursion

Recursion with subroutines should be possible. To enable this, when the compiler executes the actual subroutine functions and collects their ASTs, it must be able to identify all the referenced subroutines in that subroutine without them executing, just like a call into a first-level subroutine. Then the same logic can be used to find all referenced subroutines in that subroutine, etc., until no new subroutines are referenced.

Inlining Subroutines

It would likely be a good optimization to identify subroutines that can be inlined directly into the caller code instead of being a legitimate subroutine. I expect many users will want to create subroutines for readability, not out of necessity.

Invalid TEAL program when the main program is `Cond`

When the main program is Cond, a label is generated to jump to the end of the program. However, this is not allowed by TEAL.

For example, using the following simple.py script:

from pyteal import *

t = Cond(
        [Int(1) == Int(1), Int(2) == Int(2)],
        [Int(3) == Int(3), Int(4) == Int(4)]
        )

print(t.teal())

We have:

$ python3 simple.py > simple.teal
$ goal clerk send --from-program simple.teal --to GCBRBIIMS7IVTHC3Y7ML6WAGGPK4L3LVSHDWXPTMD5YO2E7CFWNN5X2KOE --amount 10
Couldn't broadcast tx with algod: HTTP 400 Bad Request:  32 bnz target beyond end of program

where simple.teal is:

int 1
int 1
==
bnz l0
int 3
int 3
==
bnz l1
err
int 1
bnz l2
pop
l1:
int 4
int 4
==
l2:
int 1
bnz l3
pop
l0:
int 2
int 2
==
l3:

One solution would be to add a no-op at the end. There does not seem any single-op no-op in TEAL currently, but it can be simulated using a double op:

dup
pop

Support for wide (128-bit) arithmetic

Summary

TEAL has the opcodes mulw, addw, and divmodw, which can be used to multiply, add, and divide 64-bit unsigned integers without worrying about possible overflow. They work by using two 64-bit integers to represent a single 128-bit integer on the stack.

PyTeal could expose these opcodes in order to create convenience methods for math operations whose input and output are 64-bit integers, but whose intermediate values might be as large as a 128-bit integer. (By this I mean we could make it easier to express A * B / C where A * B might overflow a 64-bit int, but the final expression will not overflow. If you're looking for ways to directly handle integers larger than 64-bits, consider byteslice arithmetic instead.)

Scope

Create a set of functions which internally use mulw, addw, and divmodw to perform multi-step arithmetic expressions on 64-bit unsigned integers. These could be:

  • WideRatio(numeratorFactors: List[Expr], denominatorFactors: List[Expr]) -> Expr: calculate the product of numeratorFactors divided by the product of denominatorFactors, e.g. (N_1 * N_2 * N_3 * ...) / (D_1 * D_2 * D_3 * ...)
  • WideSummation(positiveTerms: List[Expr], negativeTerms: List[Expr]) -> Expr: calculate the summation of all positiveTerms minus the summation of all negativeTerms, e.g. (P_1 + P_2 + P_3 + ...) - (N_1 + N_2 + N_3 + ...)

Maybe there's a need for expressions mixing addition and division too, possibly of the form (A + B) / C?

Priority

Not urgent because it's possible to use byteslice arithmetic in place of this (e.g. Btoi(BytesDiv(BytesMul(Itob(Int(1)), Itob(Int(2))), Itob(Int(3)))) expresses 1 * 2 / 3 in a way that's safe from overflow). However, exposing support for divmodw and friends would be more efficient in terms of opcode cost.

Add a way to version to PyTeal programs

Summary

For security reasons, PyTeal programs should be written for a specific version of TEAL. This is because new TEAL versions can introduce new fields and transaction types that are not accessible from older versions and which can be dangerous. For example, TEAL v2 introduced rekeying, so to secure all TEAL v1 programs, the network automatically rejects transactions that involve rekeying and TEAL v1 programs.

When future versions of TEAL are released, PyTeal will be updated to support them, so the package should provide some way to version programs. This will guard against a program being compiled to a later version of TEAL than the program author intended.

Thanks to @fabrice102 for bringing this up.

Scope

Some ideas about how this could be achieved:

  • Add a version parameter to the compileTeal method.
  • Add a Version object to the AST to declare support versions inside of a program's AST.

Urgency

This should be implemented before PyTeal supports TEAL v3.

Node contact error

I am implementing a stateless swap using PyTeal. I created a repository for the code I am developing. I am running the following from the command line, in the directory with the stateless_swap.teal file:

goal clerk compile stateless_swap.teal

However, I am getting the following error:

Cannot contact Algorand node: open /Users/brianhaney/node/data/algod.net: no such file or directory

I checked the/Users/brianhaney/node/data directory and there is no file algod.net. I am not sure whether this is something I need to download, or if there is another work around. I'd appreciate any advice or suggestions - thanks!

The module `pyteal.ast.int` shadows the Python keyword `int`

When importing pyteal by from pyteal import * (as in the documentation examples), the module pyteal.ast.int shadows the Python keyword int.

In particular, we have

$ python3 -c "from pyteal import *; print(int(5))"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: 'module' object is not callable

while:

$ python3 -c "print(int(5))"
5

Requirements

Stop submodules from being exported, small change. They shouldn't be exported in the first place

Urgency

Could be a major blocker for some people and it's a simple change that we can just knock out.

Add bit shift operations

Summary

Add left and right bit shift operator overloads to PyTeal.

Scope

While TEAL does not support bit shifts, they can be achieved by using multiplication and division. Special care will need to be taken to warn users that an overflow error will occur if a number is left shifted too much though.

Additionally, since most users probably want to use bit shift operators to read and write individual bits from uint64 and byte slices, we could provide explicit bit setter and getter operations. Thanks @jannotti for the idea.

Urgency

This would be nice to do before the next release.

Support for creating inner transactions

Summary

algorand/go-algorand#2661 introduced application actions, aka inner transactions which application can create. This issue is to expose that functionality in PyTeal as well.

Scope

Due to the simplicity of the tx_begin, tx_field, and tx_submit opcodes, the design is also pretty simple.

Four new functions should be created:

  1. InnerTxnBuilder.Begin(): This will begin an inner transaction. It will evaluate to the tx_begin opcode.

  2. InnerTxnBuilder.SetField(field: TxnField, value: Expr): This will set a single field on an inner transaction. This must be called after InnerTxnBuilder.Begin() to be a valid operation. The field argument must be from the existing TxnField enum. The value argument must be a PyTeal expression which evaluates to a type that is compatible with the indicated field type. The compiler will throw an error if the types do not match, for instance if a byteslice is set to the TxnField.amount field.

  3. InnerTxnBuilder.SetFields(values: Dict[TxnField, Expr]): This function is similar to InnerTxnBuilder.SetField, except it can accept multiple fields and values for convenience. The values argument must be a dictionary from TxnField to PyTeal expressions. Note that starting in Python 3.6 (which is coincidentally the lowest version PyTeal supports), dictionaries have a defined order, so this will not introduce any nondeterminism.

  4. InnerTxnBuilder.Submit(): This will submit an inner transaction. It will evaluate to the tx_submit opcode.

PyTeal will not do any validation of inner transaction fields beyond the simple typechecking described above. This means that when the AVM adds support for more inner transaction features (e.g. the acfg txn type), PyTeal does not need to be updated.

Example

A PyTeal-ized version of https://github.com/algorand/go-algorand/blob/e32b2c2ef9423526bbec7d749d912d3a6153410b/test/scripts/e2e_subs/tealprogs/app-escrow.teal#L116-L142

withdraw_amount = Btoi(Txn.application_args[1])
handle_withdraw = Seq(
    App.localPut(Int(0), balance_key, App.localGet(Int(0), balance_key) - withdraw_amount),
    InnerTxnBuilder.Begin(),
    # set one field
    InnerTxnBuilder.SetField(TxnField.type_enum, TxnType.Payment),
    # set multiple fields at once
    InnerTxnBuilder.SetFields({
        TxnField.amount: withdraw_amount,
        TxnField.receiver: Txn.sender()
    }),
    InnerTxnBuilder.Submit(),
    Approve(),
)

Priority

Needed for TEAL 5 support.

[TEAL 5] Add `AppParam` class

Summary

A new opcode, app_params_get, will be added to TEAL v5 (from this PR: algorand/go-algorand#2301). PyTeal should expose this opcode with a new AppParam class, similar to the current AssetParam class.

Scope

  • Create and test a new AppParam class that exposes the app_params_get opcode.
  • Since this is the first part of TEAL v5 that will be added to PyTeal, the following files will need to be modified as well:

Document compiler and IR

Summary

The internals of PyTeal could be better documented with additional README files.

Scope

  • Document the PyTeal IR and control flow graph
  • Document the compilation process

Issue with Itob function in PyTEAL

Consider the following:

Statement 1:
App.globalPut(Concat(Bytes("1"), Bytes("data_")), Int(1))

Statement 2:
App.globalGet(Concat(Itob(Int(1)), Bytes("data_")))

Statement 2 results in an error:
"type of data is incorrect. Expected object but got bigint at line 325"

To isolate this error, we tried it in an isolated environment and it results in a similar error.
Could you please help with this.

Update signature deploy examples

Summary

Currently the examples for deploying signature mode smart contracts call goal directly. They would be more helpful if they used the Python SDK, since it can now support calling the compile endpoint.

Scope

Need to update examples/signature/periodic_payment_deploy.py and examples/siguature/recurring_swap_deploy.py and test that they still work correctly.

Urgency

These are the only examples of how to deploy stateless PyTeal smart contracts, so it would help developers if they used the current best practices. That being said, more of a nice-to-have. Not much of a blocker currently

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.