Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Builtin Keccak256 #144

Merged
merged 38 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
611f785
WIP implementing basic keccak and test
jkktom Oct 20, 2023
9242af2
Merge branch 'main' into builtin-keccak
jkktom Oct 23, 2023
24bd9a1
WIP add util u128 split
jkktom Oct 23, 2023
4c96a76
WIP add util addPadding
jkktom Oct 23, 2023
2b88a89
WIP split u256
jkktom Oct 23, 2023
aabe3e9
add util splitting U256 into U64
jkktom Oct 23, 2023
986daa9
WIP keccak input
jkktom Oct 24, 2023
e751292
WIP cairo keccak test
jkktom Oct 25, 2023
3c1c5eb
WIP testing
jkktom Oct 25, 2023
51ee938
for draft pr
jkktom Oct 26, 2023
2257076
Merge branch 'main' into builtin-keccak
jkktom Oct 26, 2023
e572130
Merge branch 'main' into builtin-keccak
jkktom Oct 26, 2023
420ece7
WIP inputLE
jkktom Oct 27, 2023
0d9b696
WIP input LE tesing
jkktom Oct 30, 2023
73a0f8f
LE part done. Moving on to BE part
jkktom Oct 30, 2023
e4bcf89
implement BE input hashing and test for it
jkktom Oct 30, 2023
e2b2d51
Merge branch 'main' into builtin-keccak
jkktom Oct 30, 2023
6a31ed9
Merge branch 'main' into builtin-keccak
jkktom Oct 30, 2023
237a827
add keccak f1600
jmjac Nov 1, 2023
b8a3579
Fix linter eDrrors
jmjac Nov 1, 2023
4205eb8
Clean up comments and ready for PR
jkktom Nov 2, 2023
53fcc87
WIP resolving hintrunner MaxU128
jkktom Nov 2, 2023
96d95b9
Minor change code cleanup
jkktom Nov 2, 2023
16ef95e
Merge main branch development
jkktom Nov 2, 2023
0746710
Merge branch 'main' into builtin-keccak
jkktom Nov 2, 2023
0e4acb8
pkg/utils merge
jkktom Nov 2, 2023
997990e
Add keccak to builtin runner
jmjac Nov 6, 2023
9dbe062
Change to orignal keccakf1600 code
jmjac Nov 7, 2023
79e6578
Refactor
jmjac Nov 7, 2023
fcc3ab1
Merge branch 'main' into builtin-keccak
jkktom Nov 7, 2023
4e8baf1
Add keccak integration tests
jmjac Nov 7, 2023
df107bd
remove unnecessary codes for keccak
jkktom Nov 7, 2023
84864e1
Merge branch 'main' into builtin-keccak
jkktom Nov 7, 2023
bb35af2
revive Keccak256 Functions in Go
jkktom Nov 8, 2023
6d7f68c
rebasing
jkktom Nov 8, 2023
a19283a
Reviving Keccak function codes
jkktom Nov 8, 2023
861303c
Add comments for explanation of Keccak codes
jkktom Nov 8, 2023
97ffe20
Move to utils
jmjac Nov 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions integration_tests/builtin_tests/keccak_test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
%builtins output keccak
from starkware.cairo.common.cairo_builtins import KeccakBuiltin
from starkware.cairo.common.keccak_state import KeccakBuiltinState

func main{output_ptr: felt*, keccak_ptr: KeccakBuiltin*}() {
assert keccak_ptr[0].input = KeccakBuiltinState(68, 21, 35, 74, 43, 13, 31, 37);
let result = keccak_ptr[0].output;
let keccak_ptr = keccak_ptr + KeccakBuiltin.SIZE;
assert [output_ptr] = result.s0;
assert [output_ptr+1] = result.s1;
assert [output_ptr+2] = result.s2;
assert [output_ptr+3] = result.s3;
assert [output_ptr+4] = result.s4;
assert [output_ptr+5] = result.s5;
assert [output_ptr+6] = result.s6;
assert [output_ptr+7] = result.s7;
let output_ptr = output_ptr + 8;
return ();
}
11 changes: 11 additions & 0 deletions integration_tests/cairozero_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,14 @@ func TestPedersen(t *testing.T) {

clean("./builtin_tests/")
}

func TestKeccak(t *testing.T) {
compiledOutput, err := compileZeroCode("./builtin_tests/keccak_test.cairo")
require.NoError(t, err)

_, _, output, err := runVm(compiledOutput)
require.NoError(t, err)
require.Contains(t, output, "Program output:\n\t1304102964824333531548398680304964155037696012322029952943772\n\t688749063493959345342507274897412933692859993314608487848187\n\t986714560881445649520443980361539218531403996118322524237197\n\t1184757872753521629808292433475729390634371625298664050186717\n\t719230200744669084408849842242045083289669818920073250264351\n\t1543031433416778513637578850638598357854418012971636697855068\n\t63644822371671650271181212513090078620238279557402571802224\n\t879446821229338092940381117330194802032344024906379963157761\n")

clean("./builtin_tests/")
}
188 changes: 188 additions & 0 deletions pkg/utils/keccak.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package utils

import (
"encoding/binary"
"fmt"

"github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)

const (
KECCAK_FULL_RATE_IN_U64S = 17
KECCAK_FULL_RATE_IN_BYTES = 136
BYTES_IN_U64_WORD = 8
)

// U128Split splits a uint128 (represented as a uint256.Int) into two uint64s.
func U128Split(input *uint256.Int) (high, low uint64) {
low = input.Uint64()
input.Rsh(input, 64)
high = input.Uint64()
return
}

// Keccak256 computes the Keccak-256 hash of the input data.
func Keccak256(data []byte) ([]byte, error) {
hasher := sha3.NewLegacyKeccak256()
if _, err := hasher.Write(data); err != nil {
return nil, err // Return the error to the caller.
}
return hasher.Sum(nil), nil // Return the hash and a nil error.
}

// ConvertToByteData converts a slice of uint64 to a slice of byte.
func ConvertToByteData(input []uint64) []byte {
byteData := make([]byte, len(input)*8) // 8 bytes per uint64
for i, word := range input {
binary.LittleEndian.PutUint64(byteData[i*8:], word)
}

// To verify from web https://emn178.github.io/online-tools/keccak_256.html
//fmt.Printf("ByteData result after padding, before Keccak256 hashing: \n%x\n", byteData)

return byteData
}

func CairoKeccak(input []uint64, lastInputWord uint64, lastInputNumBytes int) ([]byte, error) {
//Add Padding to the input
paddedInput, err := AddPadding(input, lastInputWord, lastInputNumBytes)
if err != nil {
return nil, err
}
//convert from uint64 to byte slice and then apply Keccak
return Keccak256(ConvertToByteData(paddedInput))
}

func AddPadding(input []uint64, lastInputWord uint64, lastInputNumBytes int) ([]uint64, error) {
//fmt.Printf("input before AddPadding conversion in addpadding: %x\n ", input)
wordsDivisor := KECCAK_FULL_RATE_IN_U64S
lastBlockNumFullWords := len(input) % wordsDivisor

var firstWordToAppend, firstPaddingBytePart, r uint64
if lastInputNumBytes == 0 {
firstWordToAppend = 1
} else {
switch lastInputNumBytes {
case 1:
firstPaddingBytePart = 0x100
case 2:
firstPaddingBytePart = 0x10000
case 3:
firstPaddingBytePart = 0x1000000
case 4:
firstPaddingBytePart = 0x100000000
case 5:
firstPaddingBytePart = 0x10000000000
case 6:
firstPaddingBytePart = 0x1000000000000
case 7:
firstPaddingBytePart = 0x100000000000000
default:
return nil, fmt.Errorf("keccak last input word >7b")
}
r = lastInputWord % firstPaddingBytePart
firstWordToAppend = firstPaddingBytePart + r
}

// Debug print statements:
// fmt.Printf("firstPaddingBytePart: %x\n", firstPaddingBytePart)
// fmt.Printf("r: %x\n", r)
// fmt.Printf("firstWordToAppend: %x\n", firstWordToAppend)

inputAfterPadding := append(input, firstWordToAppend)

if lastBlockNumFullWords == KECCAK_FULL_RATE_IN_U64S-1 {
inputAfterPadding = append(inputAfterPadding, 0x8000000000000000+firstWordToAppend)
return inputAfterPadding, nil
}

//Debug when error
// fmt.Println("Before finalizePadding:", inputAfterPadding)
finalizedPadding := finalizePadding(inputAfterPadding, uint32(KECCAK_FULL_RATE_IN_U64S-1-lastBlockNumFullWords))
// fmt.Println("After finalizePadding:", finalizedPadding)

return finalizedPadding, nil
}

func finalizePadding(input []uint64, numPaddingWords uint32) []uint64 {
for i := uint32(0); i < numPaddingWords; i++ {
if i == numPaddingWords-1 {
input = append(input, 0x8000000000000000)
} else {
input = append(input, 0)
}
}
return input
}

//Little Endian way for Keccak256

// KeccakAddU256LE appends the low and high 64-bit words of a U256 value to a keccak input.
func KeccakAddU256LE(keccakInput []uint64, value *uint256.Int) []uint64 {
valueCopy := new(uint256.Int).Set(value)
// Split the "low" 128 bits into two uint64s and append them to keccakInput.
low, high := U128Split(valueCopy)
keccakInput = append(keccakInput, low, high)

// Shift the value right by 128 bits to get the high 128 bits.
valueCopy.Rsh(valueCopy, 128)

// Split the "high" 128 bits into two uint64s and append them to keccakInput.
low, high = U128Split(valueCopy)
keccakInput = append(keccakInput, low, high)

return keccakInput
}

// 1 initializes an array (u64)
// 2 converting input into array
// 3 adds padding to the array
// 4 put that array into keccak hashing

func KeccakU256sLEInputs(inputs []uint256.Int) ([]byte, error) {
var keccakInput []uint64
for _, input := range inputs {
keccakInput = KeccakAddU256LE(keccakInput, &input)
}
return CairoKeccak(keccakInput, 0, 0)
}

//Big Endian way codes

func ReverseBytes128(value *uint256.Int) *uint256.Int {
byteSlice := value.Bytes()
reversedSlice := make([]byte, len(byteSlice))

for i, byteValue := range byteSlice {
reversedSlice[len(byteSlice)-1-i] = byteValue
}

reversed := uint256.NewInt(0)
reversed.SetBytes(reversedSlice)

return reversed
}

func KeccakAddU256BE(keccakInput []uint64, value *uint256.Int) []uint64 {
valueCopy := new(uint256.Int).Set(value)
valueHigh, valueLow := new(uint256.Int), new(uint256.Int)
valueHigh.Rsh(valueCopy, 128)
maxU128 := Uint256Max128
valueLow.And(valueCopy, &maxU128)

reversedHigh := ReverseBytes128(valueHigh)
reversedLow := ReverseBytes128(valueLow)

keccakInput = KeccakAddU256LE(keccakInput, reversedHigh)
keccakInput = KeccakAddU256LE(keccakInput, reversedLow)
return keccakInput
}

func KeccakU256sBEInputs(inputs []uint256.Int) ([]byte, error) {
var keccakInput []uint64
for _, value := range inputs {
keccakInput = KeccakAddU256BE(keccakInput, &value)
}
return CairoKeccak(keccakInput, 0, 0)
}
Loading