Keccak256 Hash creation

algorand-devrel-demo-avm1.1/demo.py at 365acf147777337003ff535ec279a4255277cddb · fabrice102/algorand-devrel-demo-avm1.1 · GitHub is an example of message / signature / signer that works.

I have used this one however, it seems like it is recovering the signer address which is not matching to the one using which I signed the message.

Here is what I’m doing

I’m signing the message (keccak hash) using privateKey of my signer wallet and then I’m getting a signature

Now, I’m passing the (hash, signature) to ecdsa recover script that you shared.

But this script provide me a different signer address which is absolutely different from the signer (account address) of my signing private key.

Can you please suggest a way to recover exact signer wallet address (or public key)?

Can you provide the message/signature/signer you have been testing the script on?
Have you tested the message/signature/signer are all working properly on Ethereum?

signature: ‘0x5272dc4f0e7fdb721d08bce261b3e3fb7fc0315b7ddc8fd4b46e8ca0298c705d5077c1a1ccc488f4f4516eaf0f149f713caba91b9e7e1e9d9d0788d0298577751c’,

hash: ‘0xfc0b3a47ab582027c585043ccaa62b0800f38f061cd454ae7e28897a7cf4397b’

signer: 0xCDE782DEE9643b02DDE8a11499ede81EC1D05dD3
privateKEY: a7d08a23f69090a53a32814da1d262c8d2728d16bce420ae143978d85a06be49

This is the error that I receive whenever I try to execute the script

(node:18654) UnhandledPromiseRejectionWarning: Error: Network request error. Received status 400: TransactionPool.Remember: transaction UCZU3QSWKMIPOCSDWM3CPQHJAMZOWV3HJ[quote="fabrice, post:23, topic:7300, full:true"]
Can you provide the message/signature/signer you have been testing the script on?
Have you tested the message/signature/signer are all working properly on Ethereum?
[/quote]

35OYOU2VZLYZWQUMNGQ: logic eval error: assert failed pc=246. Details: pc=246, opcodes=pushint 65
==
assert

This is what my entire code looks like:

from pyteal import *

# has_value = Keccak256 message hash 
# signature = eth generated signature from signer
signer = Bytes("signer")
hash = Bytes("hash")
signature = Bytes("signature")

def approval_program():

    @Subroutine(TealType.bytes)
    def eth_ecdsa_recover(hash_value, signature):
        # """
        # Equivalent of OpenZeppelin ECDSA.recover for long 65-byte Ethereum signatures
        # https://docs.openzeppelin.com/contracts/2.x/api/cryptography#ECDSA-recover-bytes32-bytes-
        # Short 64-byte Ethereum signatures require some changes to the code
        # Return a 20-byte Ethereum address
        # Note: Unless compatibility with Ethereum or another system is necessary, we highly recommend using
        # ed25519_verify instead of ecdsa on Algorand
        # WARNING: This code has NOT been audited
        # DO NOT USE IN PRODUCTION
        # """
        opup = OpUp(OpUpMode.OnCall)  # we need more budget to be able to run

        r = Extract(signature, Int(0), Int(32))
        s = Extract(signature, Int(32), Int(32))
        # The recovery ID is shifted by 27 on Ethereum
        # For non-Ethereum signatures, remove the -27 on the line below
        v = Btoi(Extract(signature, Int(64), Int(1))) - Int(27)

        return Seq(
            Assert(Len(signature) == Int(65)),
            Assert(Len(hash_value) == Int(32)),
            opup.ensure_budget(Int(2500)),  # need 2000 for EcdsaRecover, adding a bit more for margin
            # The following two asserts are to prevent malleability
            # like in
            # https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fbf494511fd522b931f7f92e2df87d671ea8b0b/contracts/utils/cryptography/ECDSA.sol#L153
            Assert(BytesLe(s, Bytes("base16", "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0"))),
            Assert(Or(v == Int(0), v == Int(1))),
            EcdsaRecover(EcdsaCurve.Secp256k1, hash_value, v, r, s).outputReducer(
                # Ethereum concatenate the x and y part of the public key
                # and then applies Keccak256 and take the last 20 bytes
                lambda x, y: Extract(Keccak256(Concat(x, y)), Int(12), Int(20))
            )
        )

    # CONSTRUCTOR

    hash = Txn.application_args[0]
    on_creation = Seq(
        Assert(Global.group_size() == Int(1)),
        App.globalPut(hash, hash),
        Approve(),
    )

    hash = Bytes("fc0b3a47ab582027c585043ccaa62b0800f38f061cd454ae7e28897a7cf4397b")
    signature = Bytes("5272dc4f0e7fdb721d08bce261b3e3fb7fc0315b7ddc8fd4b46e8ca0298c705d5077c1a1ccc488f4f4516eaf0f149f713caba91b9e7e1e9d9d0788d0298577751c")
    on_ecdsa_recover = Seq([
        App.globalPut(hash, hash),
        App.globalPut(signer, eth_ecdsa_recover(hash, signature)),
        Approve(),
    ])

    on_call_method = Txn.application_args[0]
    on_call = Cond(
    # Owner Only operations
        [on_call_method == Bytes("ecdsa-recover"), on_ecdsa_recover],
    )
    on_delete = Seq([ 
        Approve(),
    ])
    on_update = Seq([ 
        Reject(),
    ])

    program = Cond(
        [Txn.application_id() == Int(0), on_creation],
        # All General Application calls will be routed here.
        [Txn.on_completion() == OnComplete.NoOp, on_call],
            # Reject DELETE and UPDATE Application Calls.
        [Txn.on_completion() == OnComplete.UpdateApplication, on_update],
        [Txn.on_completion() == OnComplete.DeleteApplication, on_delete]
    )
    return compileTeal(program, Mode.Application, version=6)

print(approval_program())

I tried to pass the signature & hash both with 0x and without 0x but nothing worked. All I get is pushint65 or even if it works then the signer is not correct.

is not correct according to eth-crypto JS library:

const EthCrypto = require('eth-crypto');

const signer2 = EthCrypto.recoverPublicKey(
    '0x5272dc4f0e7fdb721d08bce261b3e3fb7fc0315b7ddc8fd4b46e8ca0298c705d5077c1a1ccc488f4f4516eaf0f149f713caba91b9e7e1e9d9d0788d0298577751c', // signature
    "0xfc0b3a47ab582027c585043ccaa62b0800f38f061cd454ae7e28897a7cf4397b"
);
console.log("public key recovered:" + signer2);
console.log("address recovered:" + EthCrypto.publicKey.toAddress(signer2));

as it outputs:

public key recovered:15574fdafcf0658d145e0f9a6f71b22ded7896767124075357c810f7db17ea87fe527374483de38ccd4cf28073fd0b29f59165c810d9fa2992500d39924d4771
address recovered:0x6462141651CD84015Dd52D3f6573D1417b809B23

which is exactly what the TEAL code is outputing.

How about this error? As. Im unable to pass the signature/hash

When I do this, it simply returns pushin65 error on signature and pushin32 on hash

Why so?

Please read my js code and see whats wrong I’m doing while passing the signature and hash

Also my signer address is the one that I shared with you. The hash message is signed by ETH.sign() and signature is created. It should be correct