def test_while_break(): i = pt.ScratchVar() i.store(pt.Int(0)) items = [ i.load() < pt.Int(2), i.store(i.load() + pt.Int(1)), pt.If(i.load() == pt.Int(1), pt.Break()), ] expr = pt.While(items[0]).Do(pt.Seq(items[1], items[2])) assert expr.type_of() == pt.TealType.none assert not expr.has_return() options.enterLoop() expected, condEnd = items[0].__teal__(options) do, doEnd = pt.Seq([items[1], items[2]]).__teal__(options) expectedBranch = pt.TealConditionalBlock([]) end = pt.TealSimpleBlock([]) expectedBranch.setTrueBlock(do) expectedBranch.setFalseBlock(end) condEnd.setNextBlock(expectedBranch) doEnd.setNextBlock(expected) breakBlocks, _ = options.exitLoop() for block in breakBlocks: block.setNextBlock(end) actual, _ = expr.__teal__(options) assert actual == expected
def test_ecdsa_verify_basic(curve: pt.EcdsaCurve): args = [pt.Bytes("data"), pt.Bytes("sigA"), pt.Bytes("sigB")] pubkey = (pt.Bytes("X"), pt.Bytes("Y")) expr = pt.EcdsaVerify(curve, args[0], args[1], args[2], pubkey) assert expr.type_of() == pt.TealType.uint64 expected = pt.TealSimpleBlock([ pt.TealOp(args[0], pt.Op.byte, '"data"'), pt.TealOp(args[1], pt.Op.byte, '"sigA"'), pt.TealOp(args[2], pt.Op.byte, '"sigB"'), pt.TealOp(pubkey[0], pt.Op.byte, '"X"'), pt.TealOp(pubkey[1], pt.Op.byte, '"Y"'), pt.TealOp(expr, pt.Op.ecdsa_verify, curve.arg_name), ]) actual, _ = expr.__teal__(curve_options_map[curve]) actual.addIncoming() actual = pt.TealBlock.NormalizeBlocks(actual) assert actual == expected # compile without errors this is necessary so assembly is also tested pt.compileTeal( pt.Seq(pt.Pop(expr), pt.Approve()), pt.Mode.Application, version=curve.min_version, ) with pytest.raises(pt.TealInputError): pt.compileTeal( pt.Seq(pt.Pop(expr), pt.Approve()), pt.Mode.Application, version=curve.min_version - 1, )
def test_ecdsa_decompress(curve: pt.EcdsaCurve): compressed_pubkey = pt.Bytes("XY") pubkey = pt.EcdsaDecompress(curve, compressed_pubkey) assert pubkey.type_of() == pt.TealType.none expected = pt.TealSimpleBlock([ pt.TealOp(compressed_pubkey, pt.Op.byte, '"XY"'), pt.TealOp(pubkey, pt.Op.ecdsa_pk_decompress, curve.arg_name), pt.TealOp(pubkey.output_slots[1].store(), pt.Op.store, pubkey.output_slots[1]), pt.TealOp(pubkey.output_slots[0].store(), pt.Op.store, pubkey.output_slots[0]), ]) actual, _ = pubkey.__teal__(curve_options_map[curve]) actual.addIncoming() actual = pt.TealBlock.NormalizeBlocks(actual) with pt.TealComponent.Context.ignoreExprEquality(): assert actual == expected # compile without errors this is necessary so assembly is also tested pt.compileTeal(pt.Seq(pubkey, pt.Approve()), pt.Mode.Application, version=curve.min_version) with pytest.raises(pt.TealInputError): pt.compileTeal( pt.Seq(pubkey, pt.Approve()), pt.Mode.Application, version=curve.min_version - 1, )
def test_for(): i = pt.ScratchVar() items = [ (i.store(pt.Int(0))), i.load() < pt.Int(10), i.store(i.load() + pt.Int(1)), pt.App.globalPut(pt.Itob(i.load()), i.load() * pt.Int(2)), ] expr = pt.For(items[0], items[1], items[2]).Do(pt.Seq([items[3]])) assert expr.type_of() == pt.TealType.none assert not expr.has_return() expected, varEnd = items[0].__teal__(options) condStart, condEnd = items[1].__teal__(options) stepStart, stepEnd = items[2].__teal__(options) do, doEnd = pt.Seq([items[3]]).__teal__(options) expectedBranch = pt.TealConditionalBlock([]) end = pt.TealSimpleBlock([]) varEnd.setNextBlock(condStart) doEnd.setNextBlock(stepStart) expectedBranch.setTrueBlock(do) expectedBranch.setFalseBlock(end) condEnd.setNextBlock(expectedBranch) stepEnd.setNextBlock(condStart) actual, _ = expr.__teal__(options) assert actual == expected
def test_opup_explicit(): mode = OpUpMode.Explicit with pytest.raises(pt.TealInputError) as err: opup = OpUp(mode) assert "target_app_id must be specified in Explicit OpUp mode" in str( err.value) with pytest.raises(pt.TealTypeError): opup = OpUp(mode, pt.Bytes("appid")) opup = OpUp(mode, pt.Int(1)) with pytest.raises(pt.TealTypeError): opup.ensure_budget(pt.Bytes("budget")) with pytest.raises(pt.TealTypeError): opup.maximize_budget(pt.Bytes("fee")) assert opup.target_app_id == pt.Int(1) # verify correct usage doesn't cause an error _ = pt.Seq(opup.ensure_budget(pt.Int(500) + pt.Int(1000)), pt.Return(pt.Int(1))) _ = pt.Seq(opup.maximize_budget(pt.Txn.fee() - pt.Int(100)), pt.Return(pt.Int(1)))
def test_on_complete_action(): with pytest.raises(pt.TealInputError) as contradict_err: pt.OnCompleteAction(action=pt.Seq(), call_config=pt.CallConfig.NEVER) assert "contradicts" in str(contradict_err) assert pt.OnCompleteAction.never().is_empty() assert pt.OnCompleteAction.call_only(pt.Seq()).call_config == pt.CallConfig.CALL assert pt.OnCompleteAction.create_only(pt.Seq()).call_config == pt.CallConfig.CREATE assert pt.OnCompleteAction.always(pt.Seq()).call_config == pt.CallConfig.ALL
def test_seq_invalid(): with pytest.raises(pt.TealTypeError): pt.Seq([pt.Int(1), pt.Pop(pt.Int(2))]) with pytest.raises(pt.TealTypeError): pt.Seq([pt.Int(1), pt.Int(2)]) with pytest.raises(pt.TealTypeError): pt.Seq([pt.Seq([pt.Pop(pt.Int(1)), pt.Int(2)]), pt.Int(3)])
def test_seq_zero(): for expr in (pt.Seq(), pt.Seq([])): assert expr.type_of() == pt.TealType.none assert not expr.has_return() expected = pt.TealSimpleBlock([]) actual, _ = expr.__teal__(options) assert actual == expected
def test_wrap_handler_bare_call(): BARE_CALL_CASES = [ dummy_doing_nothing, safe_clear_state_delete, pt.Approve(), pt.Log(pt.Bytes("message")), ] for bare_call in BARE_CALL_CASES: wrapped: pt.Expr = ASTBuilder.wrap_handler(False, bare_call) expected: pt.Expr match bare_call: case pt.Expr(): if bare_call.has_return(): expected = bare_call else: expected = pt.Seq(bare_call, pt.Approve()) case pt.SubroutineFnWrapper() | pt.ABIReturnSubroutine(): expected = pt.Seq(bare_call(), pt.Approve()) case _: raise pt.TealInputError("how you got here?") wrapped_assemble = assemble_helper(wrapped) wrapped_helper = assemble_helper(expected) with pt.TealComponent.Context.ignoreExprEquality(): assert wrapped_assemble == wrapped_helper ERROR_CASES = [ ( pt.Int(1), f"bare appcall handler should be TealType.none not {pt.TealType.uint64}.", ), ( returning_u64, f"subroutine call should be returning TealType.none not {pt.TealType.uint64}.", ), ( mult_over_u64_and_log, "subroutine call should take 0 arg for bare-app call. this subroutine takes 2.", ), ( eine_constant, f"abi-returning subroutine call should be returning void not {pt.abi.Uint64TypeSpec()}.", ), ( take_abi_and_log, "abi-returning subroutine call should take 0 arg for bare-app call. this abi-returning subroutine takes 1.", ), ( 1, "bare appcall can only accept: none type Expr, or Subroutine/ABIReturnSubroutine with none return and no arg", ), ] for error_case, error_msg in ERROR_CASES: with pytest.raises(pt.TealInputError) as bug: ASTBuilder.wrap_handler(False, error_case) assert error_msg in str(bug)
def string_mult(s: pt.ScratchVar, n): i = pt.ScratchVar(pt.TealType.uint64) tmp = pt.ScratchVar(pt.TealType.bytes) start = pt.Seq(i.store(pt.Int(1)), tmp.store(s.load()), s.store(pt.Bytes(""))) step = i.store(i.load() + pt.Int(1)) return pt.Seq( pt.For(start, i.load() <= n, step).Do(s.store(pt.Concat(s.load(), tmp.load()))), s.load(), )
def test_nested_for_compiles(): i = pt.ScratchVar() expr = pt.For(i.store(pt.Int(0)), pt.Int(1), i.store(i.load() + pt.Int(1))).Do( pt.Seq( [ pt.For(i.store(pt.Int(0)), pt.Int(1), i.store(i.load() + pt.Int(1))).Do( pt.Seq([i.store(pt.Int(0))]) ) ] ) ) assert expr.type_of() == pt.TealType.none assert not expr.has_return()
def test_seq_overloads_equivalence(): items = [ pt.App.localPut(pt.Int(0), pt.Bytes("key1"), pt.Int(1)), pt.App.localPut(pt.Int(1), pt.Bytes("key2"), pt.Bytes("value2")), pt.Pop(pt.Bytes("end")), ] expr1 = pt.Seq(items) expr2 = pt.Seq(*items) expected = expr1.__teal__(options) actual = expr2.__teal__(options) assert actual == expected
def fac_by_ref_args(): n = pt.ScratchVar(pt.TealType.uint64) return pt.Seq( pt.If( pt.Or( pt.App.id() == pt.Int(0), pt.Txn.application_args.length() == pt.Int(0), )).Then(pt.Int(1)).Else( pt.Seq( n.store(pt.Btoi(pt.Txn.application_args[0])), factorial(n), n.load(), )))
def test_seq_has_return(): exprWithReturn = pt.Seq( [ pt.App.localPut(pt.Int(0), pt.Bytes("key1"), pt.Int(1)), pt.Return(pt.Int(1)), ] ) assert exprWithReturn.has_return() exprWithoutReturn = pt.Seq( [pt.App.localPut(pt.Int(0), pt.Bytes("key1"), pt.Int(1)), pt.Int(1)] ) assert not exprWithoutReturn.has_return()
def fast_fibonacci(n): i = pt.ScratchVar(pt.TealType.uint64) a = pt.ScratchVar(pt.TealType.uint64) b = pt.ScratchVar(pt.TealType.uint64) return pt.Seq( a.store(pt.Int(0)), b.store(pt.Int(1)), pt.For(i.store(pt.Int(1)), i.load() <= n, i.store(i.load() + pt.Int(1))).Do( pt.Seq( b.store(a.load() + b.load()), a.store(b.load() - a.load()), )), a.load(), )
def logcat(some_bytes, an_int): catted = pt.ScratchVar(pt.TealType.bytes) return pt.Seq( catted.store(pt.Concat(some_bytes, pt.Itob(an_int))), pt.Log(catted.load()), catted.load(), )
def test_while_compiles(): i = pt.ScratchVar() expr = pt.While(pt.Int(2)).Do(pt.Seq([i.store(pt.Int(0))])) assert expr.type_of() == pt.TealType.none assert not expr.has_return() expr.__teal__(options)
def swap(x: pt.ScratchVar, y: pt.ScratchVar): z = pt.ScratchVar(pt.TealType.anytype) return pt.Seq( z.store(x.load()), x.store(y.load()), y.store(z.load()), )
def fn_mixed_annotations_0_with_ret(a: pt.ScratchVar, b: pt.Expr, c: pt.abi.Byte, *, output: pt.abi.Uint64) -> pt.Expr: return pt.Seq( a.store(c.get() * pt.Int(0x0FF1CE) * b), output.set(a.load()), )
def test_evaluate_subroutine_no_args(): cases = ( (pt.TealType.none, pt.Return()), (pt.TealType.uint64, pt.Int(1) + pt.Int(2)), (pt.TealType.uint64, pt.Return(pt.Int(1) + pt.Int(2))), (pt.TealType.bytes, pt.Bytes("value")), (pt.TealType.bytes, pt.Return(pt.Bytes("value"))), ) for (returnType, returnValue) in cases: def mySubroutine(): return returnValue definition = pt.SubroutineDefinition(mySubroutine, returnType) declaration = evaluate_subroutine(definition) assert isinstance(declaration, pt.SubroutineDeclaration) assert declaration.subroutine is definition assert declaration.type_of() == returnValue.type_of() assert declaration.has_return() == returnValue.has_return() options.setSubroutine(definition) expected, _ = pt.Seq([returnValue]).__teal__(options) actual, _ = declaration.__teal__(options) options.setSubroutine(None) assert actual == expected
def test_wrap_handler_method_txn_types(): wrapped: pt.Expr = ASTBuilder.wrap_handler(True, multiple_txn) actual: pt.TealBlock = assemble_helper(wrapped) args: list[pt.abi.Transaction] = [ pt.abi.ApplicationCallTransaction(), pt.abi.AssetTransferTransaction(), pt.abi.PaymentTransaction(), pt.abi.Transaction(), ] output_temp = pt.abi.Uint64() expected_ast = pt.Seq( args[0]._set_index(pt.Txn.group_index() - pt.Int(4)), pt.Assert(args[0].get().type_enum() == pt.TxnType.ApplicationCall), args[1]._set_index(pt.Txn.group_index() - pt.Int(3)), pt.Assert(args[1].get().type_enum() == pt.TxnType.AssetTransfer), args[2]._set_index(pt.Txn.group_index() - pt.Int(2)), pt.Assert(args[2].get().type_enum() == pt.TxnType.Payment), args[3]._set_index(pt.Txn.group_index() - pt.Int(1)), multiple_txn(*args).store_into(output_temp), pt.abi.MethodReturn(output_temp), pt.Approve(), ) expected = assemble_helper(expected_ast) with pt.TealComponent.Context.ignoreScratchSlotEquality(), pt.TealComponent.Context.ignoreExprEquality(): assert actual == expected assert pt.TealBlock.MatchScratchSlotReferences( pt.TealBlock.GetReferencedScratchSlots(actual), pt.TealBlock.GetReferencedScratchSlots(expected), )
def fac_by_ref_BAD(): n = pt.ScratchVar(pt.TealType.uint64) return pt.Seq( n.store(pt.Int(10)), factorial_BAD(n), n.load(), )
def test_opup_oncall(): mode = OpUpMode.OnCall opup = OpUp(mode) with pytest.raises(pt.TealTypeError): opup.ensure_budget(pt.Bytes("budget")) with pytest.raises(pt.TealTypeError): opup.maximize_budget(pt.Bytes("fee")) # verify correct usage doesn't cause an error _ = pt.Seq(opup.ensure_budget(pt.Int(500) + pt.Int(1000)), pt.Return(pt.Int(1))) _ = pt.Seq(opup.maximize_budget(pt.Txn.fee() - pt.Int(100)), pt.Return(pt.Int(1)))
def test_ecdsa_verify_recovered_pk(): curve = pt.EcdsaCurve.Secp256k1 args = [pt.Bytes("data"), pt.Int(1), pt.Bytes("sigA"), pt.Bytes("sigB")] pubkey = pt.EcdsaRecover(curve, args[0], args[1], args[2], args[3]) expr = pt.EcdsaVerify(curve, args[0], args[2], args[3], pubkey) assert expr.type_of() == pt.TealType.uint64 expected = pt.TealSimpleBlock([ pt.TealOp(args[0], pt.Op.byte, '"data"'), pt.TealOp(args[1], pt.Op.int, 1), pt.TealOp(args[2], pt.Op.byte, '"sigA"'), pt.TealOp(args[3], pt.Op.byte, '"sigB"'), pt.TealOp(pubkey, pt.Op.ecdsa_pk_recover, curve.arg_name), pt.TealOp(pubkey.output_slots[1].store(), pt.Op.store, pubkey.output_slots[1]), pt.TealOp(pubkey.output_slots[0].store(), pt.Op.store, pubkey.output_slots[0]), pt.TealOp(args[0], pt.Op.byte, '"data"'), pt.TealOp(args[1], pt.Op.byte, '"sigA"'), pt.TealOp(args[2], pt.Op.byte, '"sigB"'), pt.TealOp(pubkey.output_slots[0].load(), pt.Op.load, pubkey.output_slots[0]), pt.TealOp(pubkey.output_slots[1].load(), pt.Op.load, pubkey.output_slots[1]), pt.TealOp(expr, pt.Op.ecdsa_verify, curve.arg_name), ]) actual, _ = expr.__teal__(curve_options_map[curve]) actual.addIncoming() actual = pt.TealBlock.NormalizeBlocks(actual) with pt.TealComponent.Context.ignoreExprEquality(): assert actual == expected # compile without errors this is necessary so assembly is also tested pt.compileTeal( pt.Seq(pt.Pop(expr), pt.Approve()), pt.Mode.Application, version=curve.min_version, ) with pytest.raises(pt.TealInputError): pt.compileTeal( pt.Seq(pt.Pop(expr), pt.Approve()), pt.Mode.Application, version=curve.min_version - 1, )
def factorial_BAD(n: pt.ScratchVar): tmp = pt.ScratchVar(pt.TealType.uint64) return (pt.If(n.load() <= pt.Int(1)).Then(n.store(pt.Int(1))).Else( pt.Seq( tmp.store(n.load() - pt.Int(1)), factorial_BAD(tmp), n.store(n.load() * tmp.load()), )))
def plus_one(n: pt.ScratchVar): tmp = pt.ScratchVar(pt.TealType.uint64) return (pt.If(n.load() == pt.Int(0)).Then(n.store(pt.Int(1))).Else( pt.Seq( tmp.store(n.load() - pt.Int(1)), plus_one(tmp), n.store(tmp.load() + pt.Int(1)), )))
def subr_string_mult(s: pt.ScratchVar, n): tmp = pt.ScratchVar(pt.TealType.bytes) return (pt.If(n == pt.Int(0)).Then(s.store(pt.Bytes(""))).Else( pt.Seq( tmp.store(s.load()), subr_string_mult(s, n - pt.Int(1)), s.store(pt.Concat(s.load(), tmp.load())), )))
def test_use_seq_if_multiple_overloads_equivalence(): items = [pt.Pop(pt.Int(1)), pt.Int(2)] expr = _use_seq_if_multiple(items) expected = pt.Seq(items).__teal__(options) actual = expr.__teal__(options) assert actual == expected
def string_mult(): s = pt.ScratchVar(pt.TealType.bytes) return pt.Seq( s.store(pt.Txn.application_args[0]), subr_string_mult(s, pt.Btoi(pt.Txn.application_args[1])), pt.Log(s.load()), pt.Int(100), )
def test_continue_break(): i = pt.ScratchVar() expr = pt.For(i.store(pt.Int(0)), pt.Int(1), i.store(i.load() + pt.Int(1))).Do( pt.Seq([pt.If(pt.Int(1), pt.Break(), pt.Continue())]) ) assert expr.type_of() == pt.TealType.none assert not expr.has_return() expr.__teal__(options)