Skip to content

Commit

Permalink
Simplifies keccak256 on calldata buffers.
Browse files Browse the repository at this point in the history
  • Loading branch information
scnale committed Dec 30, 2024
1 parent 2c80b61 commit 2630b92
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 15 deletions.
10 changes: 4 additions & 6 deletions src/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,15 @@ function keccak256SliceUnchecked(
}
}

function keccak256SliceCdUnchecked(
bytes calldata encoded,
uint offset,
uint length
function keccak256Cd(
bytes calldata encoded
) pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let freeMemory := mload(FREE_MEMORY_PTR)

let sliceStart := add(encoded.offset, offset)
calldatacopy(freeMemory, sliceStart, length)
let length := encoded.length
calldatacopy(freeMemory, encoded.offset, length)

hash := keccak256(freeMemory, length)
}
Expand Down
27 changes: 18 additions & 9 deletions test/Keccak.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import { keccak256Word, keccak256SliceUnchecked, keccak256SliceCdUnchecked } from "../src/Utils.sol";
import { keccak256Word, keccak256SliceUnchecked, keccak256Cd } from "../src/Utils.sol";

contract TestKeccak is Test {
using { keccak256Word } for bytes32;
using { keccak256SliceUnchecked, keccak256SliceCdUnchecked } for bytes;
using { keccak256SliceUnchecked } for bytes;

function test_bytesShouldHashTheSame(bytes calldata data) public {
bytes32 hash = data.keccak256SliceUnchecked(0, data.length);
bytes32 hashCd = data.keccak256SliceCdUnchecked(0, data.length);
bytes32 hashCd = keccak256Cd(data);
bytes32 expectedHash = keccak256(abi.encodePacked(data));
assertEq(hash, expectedHash);
assertEq(hashCd, expectedHash);
Expand All @@ -22,19 +22,25 @@ contract TestKeccak is Test {
function test_bytesSubArrayEndShouldHashTheSame(bytes calldata data, uint seed) public {
vm.assume(data.length > 0);
uint length = seed % data.length;
bytes calldata slice = data[0 : length];

bytes32 hash = data.keccak256SliceUnchecked(0, length);
bytes32 hashCd = data.keccak256SliceCdUnchecked(0, length);
bytes32 expectedHash = keccak256(abi.encodePacked(data[0:length]));
bytes32 hashCd = keccak256Cd(slice);

bytes32 expectedHash = keccak256(abi.encodePacked(slice));
assertEq(hash, expectedHash);
assertEq(hashCd, expectedHash);
}

function test_bytesSubArrayStartShouldHashTheSame(bytes calldata data, uint seed) public {
vm.assume(data.length > 0);
uint start = seed % data.length;
bytes calldata slice = data[start : data.length];

bytes32 hash = data.keccak256SliceUnchecked(start, data.length - start);
bytes32 hashCd = data.keccak256SliceCdUnchecked(start, data.length - start);
bytes32 expectedHash = keccak256(abi.encodePacked(data[start:data.length]));
bytes32 hashCd = keccak256Cd(slice);

bytes32 expectedHash = keccak256(abi.encodePacked(slice));
assertEq(hash, expectedHash);
assertEq(hashCd, expectedHash);
}
Expand All @@ -43,9 +49,12 @@ contract TestKeccak is Test {
vm.assume(data.length > 0);
uint end = bound(seed, 1, data.length);
uint start = uint(keccak256(abi.encodePacked(seed))) % end;
bytes calldata slice = data[start : end];

bytes32 hash = data.keccak256SliceUnchecked(start, end - start);
bytes32 hashCd = data.keccak256SliceCdUnchecked(start, end - start);
bytes32 expectedHash = keccak256(abi.encodePacked(data[start:end]));
bytes32 hashCd = keccak256Cd(slice);

bytes32 expectedHash = keccak256(abi.encodePacked(slice));
assertEq(hash, expectedHash);
assertEq(hashCd, expectedHash);
}
Expand Down

0 comments on commit 2630b92

Please sign in to comment.