Skip to content

Instantly share code, notes, and snippets.

@milancermak
Last active February 24, 2022 21:00
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save milancermak/9c01aec886348bb3d522e7b5f14a281f to your computer and use it in GitHub Desktop.
Save milancermak/9c01aec886348bb3d522e7b5f14a281f to your computer and use it in GitHub Desktop.
keccak256 of uint256 in Cairo

This gist contains the following Solidity code translated to Cairo:

uint256 x = 20
uint256 out = uint256(keccak256(abi.encode(x)))

It uses the keccak256 implementation from starknet-l2-storage-verifier lib. The function deals with all the peculiarities of the necessary conversion (endianness, splitting of Uint256 to four 64-bit words and back again). There's also two helper python function to illustrate how to pass arguments in and how to deal with the result.

%lang starknet
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.bitwise import bitwise_and
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin
from starkware.cairo.common.math import unsigned_div_rem
from starkware.cairo.common.uint256 import Uint256
from lib.keccak import keccak256
from lib.swap_endianness import swap_endianness_64
const TOP = 0xffffffffffffffff0000000000000000
const BOTTOM = 0xffffffffffffffff
const SHIFT = 0x10000000000000000
@view
func hash_uint256{range_check_ptr, bitwise_ptr : BitwiseBuiltin*}(input : Uint256) -> (
out : Uint256):
alloc_locals
let (t0) = bitwise_and(input.high, TOP)
let w0 = t0 / SHIFT
let (w1) = bitwise_and(input.high, BOTTOM)
let (t2) = bitwise_and(input.low, TOP)
let w2 = t2 / SHIFT
let (w3) = bitwise_and(input.low, BOTTOM)
let word : felt* = alloc()
assert word[0] = w0
assert word[1] = w1
assert word[2] = w2
assert word[3] = w3
let (keccak_ptr : felt*) = alloc()
let (hash) = keccak256{keccak_ptr=keccak_ptr}(word, 32)
let (p0) = swap_endianness_64(hash[0], 8)
let (p1) = swap_endianness_64(hash[1], 8)
let (p2) = swap_endianness_64(hash[2], 8)
let (p3) = swap_endianness_64(hash[3], 8)
# (p0 * (1 << 64)) + p1
let high = (p0 * SHIFT) + p1
# (p2 * (1 << 64)) + p3
let low = (p2 * SHIFT) + p3
let rnd : Uint256 = Uint256(low=low, high=high)
return (rnd)
end
def as_uint256(n: int) -> tuple:
low = n & (2**128-1)
high = n >> 128
return (low, high)
def from_uint256(uint256: tuple) -> int:
return uint256[0] + (uint256[1] << 128)
# test example
@pytest.mark.asyncio
async def test_rnd(contract):
n = 0x4000c000000000f000220000000012000f80
tx_info = await contract.hash_uint256(as_uint256(n)).invoke()
out = tx_info.result.out
assert from_uint256(out) == 23577221375772213464954469177302724949126992121106576711443777859778089135604
@kelemeno
Copy link

kelemeno commented Feb 24, 2022

In line 20, you divide by BOTTOM=2^64 -1, do you not want to divide by 2^64 instead?

@milancermak
Copy link
Author

You are, of course, right @kelemeno Thank you for pointing it out. The stupid-lucky thing is that because I'm discarding the remainder, it works. Updated the gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment