Skip to content

Commit

Permalink
add keccak256 operator
Browse files Browse the repository at this point in the history
  • Loading branch information
arvidn committed May 15, 2024
1 parent 0f525e1 commit 3417eb3
Show file tree
Hide file tree
Showing 11 changed files with 1,682 additions and 22 deletions.
41 changes: 22 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ base64ct = "1.6.0"
# for secp sigs
k256 = { version = "0.13.1", features = ["ecdsa"] }
p256 = { version = "0.13.2", features = ["ecdsa"] }
# for keccak256
sha3 = "0.10.8"

[dev-dependencies]
rstest = "0.17.0"
Expand Down
4 changes: 3 additions & 1 deletion fuzz/fuzz_targets/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use clvmr::bls_ops::{
};
use clvmr::core_ops::{op_cons, op_eq, op_first, op_if, op_listp, op_raise, op_rest};
use clvmr::cost::Cost;
use clvmr::keccak256_ops::op_keccak256;
use clvmr::more_ops::{
op_add, op_all, op_any, op_ash, op_coinid, op_concat, op_div, op_divmod, op_gr, op_gr_bytes,
op_logand, op_logior, op_lognot, op_logxor, op_lsh, op_mod, op_modpow, op_multiply, op_not,
Expand All @@ -21,7 +22,7 @@ use clvmr::serde::node_from_bytes;

type Opf = fn(&mut Allocator, NodePtr, Cost) -> Response;

const FUNS: [Opf; 47] = [
const FUNS: [Opf; 48] = [
op_if as Opf,
op_cons as Opf,
op_first as Opf,
Expand Down Expand Up @@ -72,6 +73,7 @@ const FUNS: [Opf; 47] = [
// base64 operators
op_base64url_encode as Opf,
op_base64url_decode as Opf,
op_keccak256 as Opf,
];

fuzz_target!(|data: &[u8]| {
Expand Down
1,565 changes: 1,565 additions & 0 deletions op-tests/test-keccak256-generated.txt

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions op-tests/test-keccak256.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
; the format for these test cases are:
; expression => expected result | expected-cost

keccak256 "foobar" => 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e | 542
keccak256 "f" "oobar" => 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e | 702
keccak256 "f" "o" "obar" => 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e | 862
keccak256 "f" "o" "o" "bar" => 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e | 1022
keccak256 "f" "o" "o" "b" "a" "r" => 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e | 1342

keccak256 "foo" => 0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d | 536
keccak256 "fo" "o" => 0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d | 696
keccak256 "f" "o" "o" => 0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d | 856
3 changes: 2 additions & 1 deletion src/chia_dialect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::core_ops::{op_cons, op_eq, op_first, op_if, op_listp, op_raise, op_re
use crate::cost::Cost;
use crate::dialect::{Dialect, OperatorSet};
use crate::err_utils::err;
use crate::keccak256_ops::op_keccak256;
use crate::more_ops::{
op_add, op_all, op_any, op_ash, op_coinid, op_concat, op_div, op_div_fixed, op_divmod, op_gr,
op_gr_bytes, op_logand, op_logior, op_lognot, op_logxor, op_lsh, op_mod, op_modpow,
Expand Down Expand Up @@ -186,7 +187,7 @@ impl Dialect for ChiaDialect {
62..=64 if (flags & ENABLE_BASE64_OPS_OUTSIDE_GUARD) != 0 => match op {
62 => op_base64url_encode,
63 => op_base64url_decode,
// 64 => op_keccak256,
64 => op_keccak256,
_ => {
unreachable!();
}
Expand Down
32 changes: 32 additions & 0 deletions src/keccak256_ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::allocator::{Allocator, NodePtr};
use crate::cost::check_cost;
use crate::cost::Cost;
use crate::op_utils::atom;
use crate::op_utils::new_atom_and_cost;
use crate::reduction::Response;
use sha3::{Digest, Keccak256};

const KECCAK256_BASE_COST: Cost = 50;
const KECCAK256_COST_PER_ARG: Cost = 160;
const KECCAK256_COST_PER_BYTE: Cost = 2;

pub fn op_keccak256(a: &mut Allocator, mut input: NodePtr, max_cost: Cost) -> Response {
let mut cost = KECCAK256_BASE_COST;

let mut byte_count: usize = 0;
let mut hasher = Keccak256::new();
while let Some((arg, rest)) = a.next(input) {
input = rest;
cost += KECCAK256_COST_PER_ARG;
check_cost(
a,
cost + byte_count as Cost * KECCAK256_COST_PER_BYTE,
max_cost,
)?;
let blob = atom(a, arg, "keccak256")?;
byte_count += blob.as_ref().len();
hasher.update(blob);
}
cost += byte_count as Cost * KECCAK256_COST_PER_BYTE;
new_atom_and_cost(a, cost, &hasher.finalize())
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod cost;
pub mod dialect;
pub mod err_utils;
pub mod f_table;
pub mod keccak256_ops;
pub mod more_ops;
pub mod number;
pub mod op_utils;
Expand Down
4 changes: 4 additions & 0 deletions src/test_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::bls_ops::{
};
use crate::core_ops::{op_cons, op_eq, op_first, op_if, op_listp, op_raise, op_rest};
use crate::cost::Cost;
use crate::keccak256_ops::op_keccak256;
use crate::more_ops::{
op_add, op_all, op_any, op_ash, op_coinid, op_concat, op_div, op_divmod, op_gr, op_gr_bytes,
op_logand, op_logior, op_lognot, op_logxor, op_lsh, op_mod, op_modpow, op_multiply, op_not,
Expand Down Expand Up @@ -236,6 +237,8 @@ use rstest::rstest;
#[case("test-secp256r1")]
#[case("test-modpow")]
#[case("test-sha256")]
#[case("test-keccak256")]
#[case("test-keccak256-generated")]
fn test_ops(#[case] filename: &str) {
use std::fs::read_to_string;

Expand Down Expand Up @@ -291,6 +294,7 @@ fn test_ops(#[case] filename: &str) {
("modpow", op_modpow as Opf),
("base64url_encode", op_base64url_encode as Opf),
("base64url_decode", op_base64url_decode as Opf),
("keccak256", op_keccak256 as Opf),
]);

println!("Test cases from: {filename}");
Expand Down
24 changes: 24 additions & 0 deletions tools/generate-keccak-tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from eth_hash.auto import keccak
from random import randbytes, randint, seed, sample
from more_itertools import sliced

# need
# python -m pip install "eth-hash[pycryptodome]"

seed(1337)

SIZE = 100

with open("../op-tests/test-keccak256-generated.txt", "w+") as f:
f.write("; This file was generated by tools/generate-keccak-tests.py\n\n")

for i in range(SIZE):

blob = randbytes(randint(1, 30))
result = keccak(blob)

for i in range(1, len(blob) + 1):
args = sliced(blob, i)
cost = 50 + len(blob) * 2 + (len(blob)+i-1)//i * 160 + len(result) * 10
args_str = " ".join([f"0x{a.hex()}" for a in args])
f.write(f"keccak256 {args_str} => 0x{result.hex()} | {cost}\n")
16 changes: 15 additions & 1 deletion tools/src/bin/benchmark-clvm-cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ pub fn main() {
.unwrap();
let number = quote(&mut a, number);

let ops: [Operator; 19] = [
let ops: [Operator; 21] = [
Operator {
opcode: 60,
name: "modpow (modulus cost)",
Expand Down Expand Up @@ -517,6 +517,20 @@ pub fn main() {
extra: None,
flags: PER_BYTE_COST | LARGE_BUFFERS,
},
Operator {
opcode: 11,
name: "sha256",
arg: Placeholder::SingleArg(Some(g1)),
extra: None,
flags: NESTING_BASE_COST | PER_ARG_COST | PER_BYTE_COST | LARGE_BUFFERS,
},
Operator {
opcode: 64,
name: "keccak256",
arg: Placeholder::SingleArg(Some(g1)),
extra: None,
flags: NESTING_BASE_COST | PER_ARG_COST | PER_BYTE_COST | LARGE_BUFFERS,
},
];

// this "magic" scaling depends on the computer you run the tests on.
Expand Down

0 comments on commit 3417eb3

Please sign in to comment.