vyper icon indicating copy to clipboard operation
vyper copied to clipboard

Keccak256 for bytes types

Open zcor opened this issue 3 years ago • 9 comments

Version Information

  • vyper Version (output of vyper --version): 0.3.7
  • OS: osx
  • Python Version (output of python --version): Python 3.9.4

What's your issue about?

Please include information like:

Intent is to produce an address-based merkle tree for a white list (which can also play nicely with Javascript for frontend devs) -- however Vyper Keccak256 does not work on bytes types

Running the following raises a panic: image image

Converting to Bytes[20] first also triggers an error

image image

How can it be fixed?

Not a big brain Vyper dev, so not sure offhand

zcor avatar Oct 03 '22 00:10 zcor

@zcor the keccak256 function only accepts a literal string, Bytes, or bytes32. Just convert it to bytes32 instead:

# @version ^0.3.7

@external
@pure
def hash_addr(addr: address) -> bytes32:
    return keccak256(convert(addr, bytes32))

Here an example using Titanoboa: image

PS: the pure modifier should be enough for this function.

pcaversaccio avatar Oct 05 '22 14:10 pcaversaccio

@pcaversaccio it's not the same due to zero padding.

charles-cooper avatar Oct 06 '22 04:10 charles-cooper

right but it depends on how you construct the Merkle tree of course.

So let me give another couple of options (since I don't know the exact specs here):

Option 1

Solidity version:

function hashAddr(address addr) public pure returns (bytes32) {
    return keccak256(abi.encode(addr)); 
}

is the same in Vyper

@external
@pure
def hash_addr(addr: address) -> bytes32:
    return keccak256(_abi_encode(addr))

Option 2

Solidity version:

function hashAddr(address addr) public pure returns (bytes32) {
    return keccak256(abi.encodePacked(addr)); 
}

is the same in Vyper

@external
@pure
def hash_addr(addr: address) -> bytes32:
    return keccak256(slice(convert(addr, bytes32), 12, 20))

It's important to understand how Vyper converts addr to bytes32. It left-padds the resulting type. E.g. hash_addr("0x388C818CA8B9251b393131C08a736A67ccB19297") would result in 0x000000000000000000000000388c818ca8b9251b393131c08a736a67ccb19297 for convert(addr, bytes32).

If you search for Merkle proof verification functions in Vyper @zcor, I have written some code here. Disclaimer: This is however not audited.

pcaversaccio avatar Oct 06 '22 08:10 pcaversaccio

ah yes, i forgot about slice -- slice( convert(addr, bytes20), 0, 20) should work.

charles-cooper avatar Oct 06 '22 16:10 charles-cooper

Brilliant, thank you!

zcor avatar Oct 06 '22 16:10 zcor

ah yes, i forgot about slice -- slice( convert(addr, bytes20), 0, 20) should work.

You must use bytes32 since slice doesn't accept anything else than Bytes, bytes32, or String. So it's:

@external
@pure
def hash_addr(addr: address) -> bytes32:
    return keccak256(slice(convert(addr, bytes32), 12, 20))

pcaversaccio avatar Oct 06 '22 17:10 pcaversaccio

@zcor FYI, I have now also implemented Merkle tree multiproofs in 🐍 Vyper: https://github.com/pcaversaccio/snekmate/blob/main/src/utils/MerkleProofVerification.vy

@charles-cooper I assume this issue can be closed.

pcaversaccio avatar Dec 19 '22 22:12 pcaversaccio

@external
@pure
def hash_addr(addr: address) -> bytes32:
    return keccak256(_abi_encode(addr))

Option 2

Solidity version:

function hashAddr(address addr) public pure returns (bytes32) {
    return keccak256(abi.encodePacked(addr)); 
}

Hit the same issue, glad I found this thread. The second option mimics abi.encodePacked() of solidity.

#simple.vy 
@external
@view
def rough() -> bytes32:
    addr: address = 0x1aD91ee08f21bE3dE0BA2ba6918E714dA6B45836
    addr32:bytes32 = convert(addr, bytes32)
    hash:bytes32 = keccak256(slice(addr32, 12, 20))
    return hash

>>> import boa
>>> simple = boa.load("./simple.vy")
>>> a = simple.rough()
>>> hex(int.from_bytes(a, "big"))
'0x38b74bc12b97e00aeca693680c257c2a2a8a3ee73b03e5c72dd1e9fb838007f8'
>>> 

jaglinux avatar Jan 03 '23 05:01 jaglinux

@pcaversaccio not closing this as I think it's still a valid feature request -- keccak256 should work for all bytes types without the need for slicing.

charles-cooper avatar Sep 19 '23 15:09 charles-cooper