Problem with ed25519verify

Hi everyone!
As described in the title I’m having trouble using the Ed25519Verify function.
I’m not understanding the following error and even doing some research I haven’t found useful information to solve it:

algosdk.error.AlgodHTTPError: TransactionPool.Remember: transaction L5SNG7ALALAM5RMTAZDIYIBJ3TWIIXL5BMOABV3STIC2CRD4BWLQ: logic eval error: pc=383 dynamic cost budget exceeded, executing ed25519verify: local program cost was 42. Details: pc=383, opcodes=txna ApplicationArgs 2
txna ApplicationArgs 3
ed25519verify

I think the problem is that the function is computationally too expensive but also reducing the method to a single Assert with Ed25519Verify() and an Approve() does not change.

Below I report part of the code of my smart contract and the Python code i use to create the transaction:

Smart contract

    @Subroutine(TealType.none)
    def delegate_vote():
        return Seq(
            Assert(
                Ed25519Verify(Txn.application_args[1], Txn.application_args[2], Txn.application_args[3]),
            ),
            Approve()
        )

Transaction

def sign_param(hash, app_account, private_key):
    to_sign = (
        b"ProgData"
        + encoding.decode_address(app_account)
        + hash
    )
    signature = sign_bytes(to_sign, private_key)
    return signature
op = "test_ed25519verify"
param = "data"
hash = base64.b64encode(sha256(param.encode("utf-8")).digest())
sign = sign_param(hash, app_account, delegator['private_key'])
app_args = [op, hash, str(sign), delegator['address']]
vote_txn = make_app_call_txn(client, app_id, sender['address'], app_args)

In ** make_app_call_txn ** I encode of the app_args and the final result is the following:

[b’test_ed25519verify’, b’Om6weQ85rIfJTzhWst0sXREOaBFgImGpqSPTuyOtyLc=‘, b’MsmsZbtLNwycFXlfLpba4llJk0EW27OumW6jZaKWkRwBGg56QjeSujDi8JgIv14XNOsWZj4CROtwolrVQfsiBw==’, b’JG3IBNG4LBCPAVYB5VISGIS3QCL4LLFVP3K6UIKBFB3MESVRE7Y5BNYUBA’]

You can make additional “noop” app calls to increase your opcode budget, note that you’ll need to appropriately handle them in the contract and probably add some nonce to the note field since they’ll probably be identical besides that and you’ll get an error message saying something like “transaction already in ledger”

2 Likes

Thanks, your solution solved the budget opcode problem, but I still have problems running the Ed25519Verify function correctly.
In order to give you as much info as possible, I will post the entire code I’m running.

Smart contract:

from pyteal import *
from lib.pyteal_helpers import program


def approval():
    op_test_ed25519_verify = Bytes("ed25519_verify")
    op_increase_budget = Bytes("increase budget")

    @Subroutine(TealType.none)
    def test_ed25519_verify():
        return Seq(
            Assert(
                Ed25519Verify(Txn.application_args[1], Txn.application_args[2], Txn.accounts[1]),
            ),
            Approve()
        )

    @Subroutine(TealType.none)
    def increase_budget():
        return Seq(
            Approve()
        )

    return program.event(
        init=Approve(),
        delete=Approve(),
        update=Approve(),
        close_out=Approve(),
        opt_in=Approve(),
        no_op=Seq(
            Cond(
                [
                    Txn.application_args[0] == op_test_ed25519_verify,
                    test_ed25519_verify()
                ],
                [
                    Txn.application_args[0] == op_increase_budget,
                    increase_budget()
                ],
            ),
            Reject(),
        ),
    )


def clear():
    return Approve()

After compile, deploy and optin I run this Python script:

import base64

from algosdk import encoding
from algosdk.future import transaction
from hashlib import sha256
from lib.utils.accounts import load_account
from lib.utils.clients import get_algod_client
from lib.utils.transaction_composer import load_sc


def sign_data(data, app_account, private_key):
    to_sign = (
        b"ProgData"
        + encoding.decode_address(app_account)
        + data
    )
    signing_key = transaction.SigningKey(base64.b64decode(private_key)[:32])
    signed = signing_key.sign(to_sign)
    return signed.signature

def noop_call(client, sender, data_signer, sc):
    print(f'Testing Ed25519Verify...')
    #  Transaction to test Ed25519Verify
    op = "ed25519_verify"
    param = "data"
    data = sha256(param.encode('utf-8')).digest()
    signed_data = sign_data(data, sc['account'], data_signer['private_key'])
    app_args = [op.encode("utf-8"), base64.b64encode(data), base64.b64encode(signed_data)]
    sp_params = client.suggested_params()
    accounts = [data_signer['address']]
    ed25519_verify_txn = transaction.ApplicationNoOpTxn(sender['address'], sp_params, sc['id'], app_args, accounts)

    #  Transaction to increase opcode budget n.1
    op = "increase budget"
    note = "txn 1".encode("utf-8")
    app_args = [op.encode("utf-8")]
    sp_params = client.suggested_params()
    increase_budget_1_txn = transaction.ApplicationNoOpTxn(sender['address'], sp_params, sc['id'], app_args, note=note)

    #  Transaction to increase opcode budget n.2
    note = "txn 2".encode("utf-8")
    sp_params = client.suggested_params()
    increase_budget_2_txn = transaction.ApplicationNoOpTxn(sender['address'], sp_params, sc['id'], app_args, note=note)

    group_id = transaction.calculate_group_id([ed25519_verify_txn, increase_budget_1_txn, increase_budget_2_txn])
    ed25519_verify_txn.group = group_id
    increase_budget_1_txn.group = group_id
    increase_budget_2_txn.group = group_id

    signed_txn_1 = ed25519_verify_txn.sign(sender['private_key'])
    signed_txn_2 = increase_budget_1_txn.sign(sender['private_key'])
    signed_txn_3 = increase_budget_2_txn.sign(sender['private_key'])
    signed_txns = [signed_txn_1, signed_txn_2, signed_txn_3]

    tx_id = client.send_transactions(signed_txns)
    try:
        transaction_response = transaction.wait_for_confirmation(client, tx_id, 2)
    except Exception as err:
        print(err)
        return
    print(f'Test Ed25519Verify successful')


def main():
    client = get_algod_client()
    sender = load_account("master")
    data_signer = load_account("user")
    sc = load_sc('test')
    noop_call(client, sender, data_signer, sc)


if __name__ == '__main__':
    main()

These are my app_args:

[b’ed25519_verify’, b’Om6weQ85rIfJTzhWst0sXREOaBFgImGpqSPTuyOtyLc=‘, b’nHwAgzHAwbefshLVGVNtFgMrUsszk+lFLlbwy6zzbhjoshU4I0aoGLMBDwbL9iRaWqPybkSSgB5gLRRE0KlYDw==’]

This is the error:

algosdk.error.AlgodHTTPError: TransactionPool.Remember: transaction ZQCM3NINZTTIU6K2OQPPZVN5G2A2XK54FR4PJXU6FZSFI4Q47ZWA: logic eval error: invalid signature. Details: pc=129, opcodes=txna ApplicationArgs 2
txna Accounts 1
ed25519verify

I think the problem is how I sign data or the format of app_args is wrong.
The sign_data() function is an adapted version of the one you posted some time ago in another topic: https://github.com/algorand-devrel/decipher-drop/blob/main/util.py#L82-L92

I see at least 1 issue, that is using the app_addr to prefix the data, the method requires the hash of the contract not the app address. While for lsigs the address is the hash of the program, applications addresses are the hash of the app id. You can get the hash of the approval program when you compile it or you can use the new op as of AVM7 ed25519verify_bare which doesn’t require any special prefixing prior to hashing

1 Like

Thanks, I used ed25519verify_bare and with your suggestions I was able to verify the signature correctly!