Skip to content

Commit

Permalink
Added MiMC+EVM contract, which reduces gas cost of hashing
Browse files Browse the repository at this point in the history
Also added gas reporting for tests, and split MiMC hash into e5/e7
  • Loading branch information
HarryR authored and HarryR committed Jul 29, 2019
1 parent 9d42382 commit 8ffd607
Show file tree
Hide file tree
Showing 12 changed files with 361 additions and 49 deletions.
1 change: 1 addition & 0 deletions ethsnarks/mimc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .permutation import mimc, mimc_hash, mimc_hash_md
169 changes: 169 additions & 0 deletions ethsnarks/mimc/contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Copyright (c) 2018 Jordi Baylina
# Copyright (c) 2019 Harry Roberts
# License: LGPL-3.0+
# Based on: https://github.com/iden3/circomlib/blob/master/src/mimc_gencontract.js

import sys
import json
from binascii import hexlify

from ..sha3 import keccak_256
from ..evmasm import *
from ..field import SNARK_SCALAR_FIELD

from .permutation import mimc_constants


def _mimc_opcodes_round(exponent):
# x = input
# k = key
# q = field modulus
# stack upon entry: x q q k q
# stack upon exit: r k q
if exponent == 7:
return [
DUP(3), # k x q q k q
ADDMOD, # t=x+k q k q
DUP(1), # q t q k q
DUP(0), # q q t q k q
DUP(2), # t q q t q k q
DUP(0), # t t q q t q k q
MULMOD(), # a=t^2 q t q k q
DUP(1), # q a q t q k q
DUP(1), # a q a q t q k q
DUP(0), # a a q a q t q k q
MULMOD, # b=t^4 a q t q k q
MULMOD, # c=t^6 t q k q
MULMOD # r=t^7 k q
]
elif exponent == 5:
return [
DUP(3), # k x q q k q
ADDMOD, # t=x+k q k q
DUP(1), # q t q k q
DUP(0), # q q t q k q
DUP(2), # t q q t q k q
DUP(0), # t t q q t q k q
MULMOD(), # a=t^2 q t q k q
DUP(0), # a a q t q k q
MULMOD, # b=t^4 t q k q
MULMOD # r=t^5 k q
]


def mimc_contract_opcodes(exponent):
assert exponent in (5, 7)
tag = keccak_256(f"MiMCpe{exponent}(uint256,uint256)".encode('ascii')).hexdigest()

# Ensuring that `exponent ** n_rounds` > SNARK_SCALAR_FIELD
n_rounds = 110 if exponent == 5 else 91
constants = mimc_constants(R=n_rounds)

yield [PUSH(0x44), # callDataLength
PUSH(0), # callDataOffset
PUSH(0), # memoryOffset
CALLDATACOPY,
PUSH(1<<224),
PUSH(0),
MLOAD,
DIV,
PUSH(int(tag[:8], 16)), # function selector
EQ,
JMPI('start'),
INVALID]

yield [LABEL('start'),
PUSH(SNARK_SCALAR_FIELD), # q
PUSH(0x24),
MLOAD] # k q

yield [
PUSH(0x04), # 0x04 k q
MLOAD # x k q
]

for c_i in constants:
yield [
DUP(2), # q r k q
DUP(0), # q q r k q
DUP(0), # q q q r k q
SWAP(3), # r q q q k q
PUSH(c_i), # c r q q q k q
ADDMOD, # c+r q q k q
]
yield _mimc_opcodes_round(exponent)

# add k to result, then return
yield [
ADDMOD, # r+k
PUSH(0), # r+k 0
MSTORE, #
PUSH(0x20), # 0x20
PUSH(0), # 0 0x20
RETURN
]


def mimc_abi(exponent):
assert exponent in (5, 7)
return [{
"constant": True,
"inputs": [
{
"name": "in_x",
"type": "uint256"
},
{
"name": "in_k",
"type": "uint256"
}
],
"name": f"MiMCpe{exponent}",
"outputs": [
{
"name": "out_x",
"type": "uint256"
}
],
"payable": False,
"stateMutability": "pure",
"type": "function"
}]


def mimc_contract(exponent):
gen = Codegen()
for _ in mimc_contract_opcodes(exponent):
gen.append(_)
return gen.createTxData()


def main(*args):
if len(args) < 2:
print("Usage: %s <abi|contract> <exponent> [outfile]" % (args[0],))
return 1
command = args[1]
exponent = int(args[2])
if exponent not in (5, 7):
print("Error: exponent must be 5 or 7")
return 2
outfile = sys.stdout
if len(args) > 3:
outfile = open(args[3], 'wb')
if command == "abi":
outfile.write(json.dumps(mimc_abi(exponent)) + "\n")
return 0
elif command == "contract":
data = mimc_contract(exponent)
if outfile == sys.stdout:
data = '0x' + hexlify(data).decode('ascii')
outfile.write(data)
return 0
else:
print("Error: unknown command", command)
if outfile != sys.stdout:
outfile.close()


if __name__ == "__main__":
sys.exit(main(*sys.argv))
32 changes: 24 additions & 8 deletions ethsnarks/mimc.py → ethsnarks/mimc/permutation.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
# Copyright (c) 2018 HarryR
# License: LGPL-3.0+

try:
# pysha3
from sha3 import keccak_256
except ImportError:
# pycryptodome
from Crypto.Hash import keccak
keccak_256 = lambda *args: keccak.new(*args, digest_bits=256)
from ..sha3 import keccak_256
from ..field import SNARK_SCALAR_FIELD


SNARK_SCALAR_FIELD = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
DEFAULT_EXPONENT = 7
DEFAULT_ROUNDS = 91
DEFAULT_SEED = b'mimc'
Expand Down Expand Up @@ -130,6 +124,28 @@ def mimc_hash(x, k=0, seed=DEFAULT_SEED, p=SNARK_SCALAR_FIELD, e=DEFAULT_EXPONEN
return k


"""
Merkle-Damgard structure, used to turn a cipher into a one-way-compression function
m_i
|
|
v
k_{i-1} -->[E]
|
|
v
k_i
The output is used as the key for the next message
The last output is used as the result
"""
def mimc_hash_md(x, k=0, seed=DEFAULT_SEED, p=SNARK_SCALAR_FIELD, e=DEFAULT_EXPONENT, R=DEFAULT_ROUNDS):
for x_i in x:
k = mimc(x_i, k, seed, p, e, R)
return k


def _main():
import argparse
parser = argparse.ArgumentParser("MiMC")
Expand Down
7 changes: 7 additions & 0 deletions ethsnarks/sha3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
try:
# pysha3
from sha3 import keccak_256
except ImportError:
# pycryptodome
from Crypto.Hash import keccak
keccak_256 = lambda *args: keccak.new(*args, digest_bits=256)
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
{
"name": "ethsnarks",
"version": "0.1.0",
"version": "0.2.0",
"description": "zkSNARKS for Ethereum",
"main": "truffle.js",
"repository": "https://github.com/HarryR/ethsnarks.git",
"author": "[email protected]",
"license": "LGPL-3.0+",
"dependencies": {
"solc": "^0.5.9",
"truffle": "^5.0.22"
"rlp": "^2.2.3",
"solc": "^0.5.10",
"truffle": "^5.0.29"
},
"devDependencies": {
"ganache-cli": "^6.4.4",
"solhint": "^1.5.1",
"solidity-coverage": "^0.5.11"
"solidity-coverage": "^0.5.11",
"eth-gas-reporter": "^0.2.4"
},
"scripts": {
"compile": "truffle compile",
Expand Down
Loading

0 comments on commit 8ffd607

Please sign in to comment.