Skip to content

Instantly share code, notes, and snippets.

@LB-thomasp
Last active April 26, 2024 15:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LB-thomasp/1fa9ee33b13ef2ea72be57afc48be6c0 to your computer and use it in GitHub Desktop.
Save LB-thomasp/1fa9ee33b13ef2ea72be57afc48be6c0 to your computer and use it in GitHub Desktop.
Performs keccak256 hashing of value type parameters more efficiently than high-level Solidity.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// DISCLAIMER: WORK IN PROGRESS - NOT AUDITED!
/**
* @title EfficientHash
*
* @author Limit Break
*
* @notice Performs keccak256 hashing of value type parameters more efficiently than
* @notice high-level Solidity by utilizing scratch space for one or two values and
* @notice efficient utilization of memory for parameter counts greater than two.
*
* @notice Gas savings for EfficientHash compared to keccak256(abi.encode(...)):
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 1 / 67 / 67
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 5 / 66 / 66
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 10 / 58 / 58
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 15 / 1549 / 565
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 20 / 3379 / 1027
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 25 / 5807 / 1650
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 50 / 23691 / 10107
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 75 / 69164 / 41620
* @notice Parameter Count / Gas Savings (Shanghai) / Gas Savings (Cancun): 99 / 172694 / 126646
*
* @dev Notes:
* @dev - `efficientHash` is overloaded for parameter counts between one and eight.
* @dev - Parameter counts between nine and sixteen require two functions to avoid
* @dev stack too deep errors. Each parameter count has a dedicated set of functions
* @dev (`efficientHashNineStep1`/`efficientHashNineStep2` ... `efficientHashSixteenStep1`/`efficientHashSixteenStep2`)
* @dev that must both be called to get the hash.
* @dev `Step1` functions take eight parameters and return a memory pointer that is passed to `Step2`
* @dev `Step2` functions take the remaining parameters and return the hash of the values
* @dev Example:
* @dev bytes32 hash = EfficientHash.efficientHashElevenStep2(
* @dev EfficientHash.efficientHashElevenStep1(
* @dev value1,
* @dev value2,
* @dev value3,
* @dev value4,
* @dev value5,
* @dev value6,
* @dev value7,
* @dev value8
* @dev ),
* @dev value9,
* @dev value10,
* @dev value11,
* @dev );
* @dev - Parameter counts greater than sixteen must use the `Extension` functions.
* @dev Extension starts with `efficientHashExtensionStart` which takes the number
* @dev of parameters and the first eight parameters as an input and returns a
* @dev memory pointer that is passed to the `Continue` and `End` functions.
* @dev While the number of parameters remaining is greater than eight, call the
* @dev `efficientHashExtensionContinue` function with the pointer value and
* @dev the next eight values.
* @dev When the number of parameters remaining is less than or equal to eight
* @dev call the `efficientHashExtensionEnd` function with the pointer value
* @dev and remaining values.
* @dev Example:
* @dev bytes32 hash = EfficientHash.efficientHashExtensionEnd(
* @dev EfficientHash.efficientHashExtensionContinue(
* @dev EfficientHash.efficientHashExtensionStart(
* @dev 23,
* @dev value1,
* @dev value2,
* @dev value3,
* @dev value4,
* @dev value5,
* @dev value6,
* @dev value7,
* @dev value8
* @dev ),
* @dev value9,
* @dev value10,
* @dev value11,
* @dev value12,
* @dev value13,
* @dev value14,
* @dev value15,
* @dev value16
* @dev ),
* @dev value17,
* @dev value18,
* @dev value19,
* @dev value20,
* @dev value21,
* @dev value22,
* @dev value23
* @dev );
*/
library EfficientHash {
/**
* @notice Hashes one value type.
*
* @param value The value to be hashed.
*
* @return hash The hash of the value.
*/
function efficientHash(bytes32 value) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(0x00, value)
hash := keccak256(0x00, 0x20)
}
}
/**
* @notice Hashes two value types.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHash(bytes32 value1, bytes32 value2) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(0x00, value1)
mstore(0x20, value2)
hash := keccak256(0x00, 0x40)
}
}
/**
* @notice Hashes three value types.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHash(bytes32 value1, bytes32 value2, bytes32 value3) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0x40, add(ptr, 0x60))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
hash := keccak256(ptr, 0x60)
}
}
/**
* @notice Hashes four value types.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHash(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0x40, add(ptr, 0x80))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
hash := keccak256(ptr, 0x80)
}
}
/**
* @notice Hashes five value types.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHash(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0x40, add(ptr, 0xA0))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
hash := keccak256(ptr, 0xA0)
}
}
/**
* @notice Hashes six value types.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHash(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0x40, add(ptr, 0xC0))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
hash := keccak256(ptr, 0xC0)
}
}
/**
* @notice Hashes seven value types.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHash(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0x40, add(ptr, 0xE0))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
hash := keccak256(ptr, 0xE0)
}
}
/**
* @notice Hashes eight value types.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHash(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(0x40, add(ptr, 0x100))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
hash := keccak256(ptr, 0x100)
}
}
/**
* @notice Step one of hashing nine values. Must be followed by `efficientHashNineStep2` to hash the values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashNineStep1(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(ptr, 0x120))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
}
}
/**
* @notice Step two of hashing nine values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value9 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashNineStep2(
uint256 ptr,
bytes32 value9
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(add(ptr, 0x100), value9)
hash := keccak256(ptr, 0x120)
}
}
/**
* @notice Step one of hashing ten values. Must be followed by `efficientHashTenStep2` to hash the values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashTenStep1(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(ptr, 0x140))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
}
}
/**
* @notice Step two of hashing ten values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value9 Value to be hashed.
* @param value10 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashTenStep2(
uint256 ptr,
bytes32 value9, bytes32 value10
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(add(ptr, 0x100), value9)
mstore(add(ptr, 0x120), value10)
hash := keccak256(ptr, 0x140)
}
}
/**
* @notice Step one of hashing eleven values. Must be followed by `efficientHashElevenStep2` to hash the values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashElevenStep1(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(ptr, 0x160))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
}
}
/**
* @notice Step two of hashing eleven values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value9 Value to be hashed.
* @param value10 Value to be hashed.
* @param value11 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashElevenStep2(
uint256 ptr,
bytes32 value9, bytes32 value10, bytes32 value11
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(add(ptr, 0x100), value9)
mstore(add(ptr, 0x120), value10)
mstore(add(ptr, 0x140), value11)
hash := keccak256(ptr, 0x160)
}
}
/**
* @notice Step one of hashing twelve values. Must be followed by `efficientHashTwelveStep2` to hash the values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashTwelveStep1(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(ptr, 0x180))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
}
}
/**
* @notice Step two of hashing twelve values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value9 Value to be hashed.
* @param value10 Value to be hashed.
* @param value11 Value to be hashed.
* @param value12 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashTwelveStep2(
uint256 ptr,
bytes32 value9, bytes32 value10, bytes32 value11, bytes32 value12
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(add(ptr, 0x100), value9)
mstore(add(ptr, 0x120), value10)
mstore(add(ptr, 0x140), value11)
mstore(add(ptr, 0x160), value12)
hash := keccak256(ptr, 0x180)
}
}
/**
* @notice Step one of hashing thirteen values. Must be followed by `efficientHashThirteenStep2` to hash the values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashThirteenStep1(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(ptr, 0x1A0))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
}
}
/**
* @notice Step two of hashing thirteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value9 Value to be hashed.
* @param value10 Value to be hashed.
* @param value11 Value to be hashed.
* @param value12 Value to be hashed.
* @param value13 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashThirteenStep2(
uint256 ptr,
bytes32 value9, bytes32 value10, bytes32 value11, bytes32 value12,
bytes32 value13
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(add(ptr, 0x100), value9)
mstore(add(ptr, 0x120), value10)
mstore(add(ptr, 0x140), value11)
mstore(add(ptr, 0x160), value12)
mstore(add(ptr, 0x180), value13)
hash := keccak256(ptr, 0x1A0)
}
}
/**
* @notice Step one of hashing fourteen values. Must be followed by `efficientHashFourteenStep2` to hash the values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashFourteenStep1(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(ptr, 0x1C0))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
}
}
/**
* @notice Step two of hashing fourteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value9 Value to be hashed.
* @param value10 Value to be hashed.
* @param value11 Value to be hashed.
* @param value12 Value to be hashed.
* @param value13 Value to be hashed.
* @param value14 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashFourteenStep2(
uint256 ptr,
bytes32 value9, bytes32 value10, bytes32 value11, bytes32 value12,
bytes32 value13, bytes32 value14
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(add(ptr, 0x100), value9)
mstore(add(ptr, 0x120), value10)
mstore(add(ptr, 0x140), value11)
mstore(add(ptr, 0x160), value12)
mstore(add(ptr, 0x180), value13)
mstore(add(ptr, 0x1A0), value14)
hash := keccak256(ptr, 0x1C0)
}
}
/**
* @notice Step one of hashing fifteen values. Must be followed by `efficientHashFifteenStep2` to hash the values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashFifteenStep1(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(ptr, 0x1E0))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
}
}
/**
* @notice Step two of hashing fifteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value9 Value to be hashed.
* @param value10 Value to be hashed.
* @param value11 Value to be hashed.
* @param value12 Value to be hashed.
* @param value13 Value to be hashed.
* @param value14 Value to be hashed.
* @param value15 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashFifteenStep2(
uint256 ptr,
bytes32 value9, bytes32 value10, bytes32 value11, bytes32 value12,
bytes32 value13, bytes32 value14, bytes32 value15
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(add(ptr, 0x100), value9)
mstore(add(ptr, 0x120), value10)
mstore(add(ptr, 0x140), value11)
mstore(add(ptr, 0x160), value12)
mstore(add(ptr, 0x180), value13)
mstore(add(ptr, 0x1A0), value14)
mstore(add(ptr, 0x1C0), value15)
hash := keccak256(ptr, 0x1E0)
}
}
/**
* @notice Step one of hashing sixteen values. Must be followed by `efficientHashSixteenStep2` to hash the values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashSixteenStep1(
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(ptr, 0x200))
mstore(ptr, value1)
mstore(add(ptr, 0x20), value2)
mstore(add(ptr, 0x40), value3)
mstore(add(ptr, 0x60), value4)
mstore(add(ptr, 0x80), value5)
mstore(add(ptr, 0xA0), value6)
mstore(add(ptr, 0xC0), value7)
mstore(add(ptr, 0xE0), value8)
}
}
/**
* @notice Step two of hashing sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value9 Value to be hashed.
* @param value10 Value to be hashed.
* @param value11 Value to be hashed.
* @param value12 Value to be hashed.
* @param value13 Value to be hashed.
* @param value14 Value to be hashed.
* @param value15 Value to be hashed.
* @param value16 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashSixteenStep2(
uint256 ptr,
bytes32 value9, bytes32 value10, bytes32 value11, bytes32 value12,
bytes32 value13, bytes32 value14, bytes32 value15, bytes32 value16
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
mstore(add(ptr, 0x100), value9)
mstore(add(ptr, 0x120), value10)
mstore(add(ptr, 0x140), value11)
mstore(add(ptr, 0x160), value12)
mstore(add(ptr, 0x180), value13)
mstore(add(ptr, 0x1A0), value14)
mstore(add(ptr, 0x1C0), value15)
mstore(add(ptr, 0x1E0), value16)
hash := keccak256(ptr, 0x200)
}
}
/**
* @notice Step one of hashing more than sixteen values.
* @notice Must be followed by at least one call to
* @notice `efficientHashExtensionContinue` and completed with
* @notice a call to `efficientHashExtensionEnd` with the remaining
* @notice values.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptr The memory pointer location for the values to hash.
*/
function efficientHashExtensionStart(
uint256 numberOfValues,
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
mstore(0x40, add(add(ptr, 0x20), mul(numberOfValues, 0x20)))
mstore(ptr, 0x100)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
mstore(add(ptr, 0x60), value3)
mstore(add(ptr, 0x80), value4)
mstore(add(ptr, 0xA0), value5)
mstore(add(ptr, 0xC0), value6)
mstore(add(ptr, 0xE0), value7)
mstore(add(ptr, 0x100), value8)
}
}
/**
* @notice Second step of hashing more than sixteen values.
* @notice Adds another eight values to the values to be hashed.
* @notice May be called as many times as necessary until the values
* @notice remaining to be added to the hash is less than or equal to
* @notice eight.
*
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return ptrReturn The memory pointer location for the values to hash.
*/
function efficientHashExtensionContinue(
uint256 ptr,
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(uint256 ptrReturn) {
assembly ("memory-safe") {
ptrReturn := ptr
let length := mload(ptrReturn)
mstore(ptrReturn, add(length, 0x100))
ptr := add(ptrReturn, length)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
mstore(add(ptr, 0x60), value3)
mstore(add(ptr, 0x80), value4)
mstore(add(ptr, 0xA0), value5)
mstore(add(ptr, 0xC0), value6)
mstore(add(ptr, 0xE0), value7)
mstore(add(ptr, 0x100), value8)
}
}
/**
* @notice Final step of hashing more than sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value1 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashExtensionEnd(
uint256 ptr,
bytes32 value1
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptrStart := ptr
let length := mload(ptrStart)
ptr := add(ptrStart, length)
length := add(length, 0x20)
mstore(add(ptr, 0x20), value1)
hash := keccak256(add(ptrStart, 0x20), length)
}
}
/**
* @notice Final step of hashing more than sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashExtensionEnd(
uint256 ptr,
bytes32 value1, bytes32 value2
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptrStart := ptr
let length := mload(ptrStart)
ptr := add(ptrStart, length)
length := add(length, 0x40)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
hash := keccak256(add(ptrStart, 0x20), length)
}
}
/**
* @notice Final step of hashing more than sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashExtensionEnd(
uint256 ptr,
bytes32 value1, bytes32 value2, bytes32 value3
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptrStart := ptr
let length := mload(ptrStart)
ptr := add(ptrStart, length)
length := add(length, 0x60)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
mstore(add(ptr, 0x60), value3)
hash := keccak256(add(ptrStart, 0x20), length)
}
}
/**
* @notice Final step of hashing more than sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashExtensionEnd(
uint256 ptr,
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptrStart := ptr
let length := mload(ptrStart)
ptr := add(ptrStart, length)
length := add(length, 0x80)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
mstore(add(ptr, 0x60), value3)
mstore(add(ptr, 0x80), value4)
hash := keccak256(add(ptrStart, 0x20), length)
}
}
/**
* @notice Final step of hashing more than sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashExtensionEnd(
uint256 ptr,
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptrStart := ptr
let length := mload(ptrStart)
ptr := add(ptrStart, length)
length := add(length, 0xA0)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
mstore(add(ptr, 0x60), value3)
mstore(add(ptr, 0x80), value4)
mstore(add(ptr, 0xA0), value5)
hash := keccak256(add(ptrStart, 0x20), length)
}
}
/**
* @notice Final step of hashing more than sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashExtensionEnd(
uint256 ptr,
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptrStart := ptr
let length := mload(ptrStart)
ptr := add(ptrStart, length)
length := add(length, 0xC0)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
mstore(add(ptr, 0x60), value3)
mstore(add(ptr, 0x80), value4)
mstore(add(ptr, 0xA0), value5)
mstore(add(ptr, 0xC0), value6)
hash := keccak256(add(ptrStart, 0x20), length)
}
}
/**
* @notice Final step of hashing more than sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashExtensionEnd(
uint256 ptr,
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptrStart := ptr
let length := mload(ptrStart)
ptr := add(ptrStart, length)
length := add(length, 0xE0)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
mstore(add(ptr, 0x60), value3)
mstore(add(ptr, 0x80), value4)
mstore(add(ptr, 0xA0), value5)
mstore(add(ptr, 0xC0), value6)
mstore(add(ptr, 0xE0), value7)
hash := keccak256(add(ptrStart, 0x20), length)
}
}
/**
* @notice Final step of hashing more than sixteen values.
*
* @param ptr The memory pointer location for the values to hash.
* @param value1 Value to be hashed.
* @param value2 Value to be hashed.
* @param value3 Value to be hashed.
* @param value4 Value to be hashed.
* @param value5 Value to be hashed.
* @param value6 Value to be hashed.
* @param value7 Value to be hashed.
* @param value8 Value to be hashed.
*
* @return hash The hash of the values.
*/
function efficientHashExtensionEnd(
uint256 ptr,
bytes32 value1, bytes32 value2, bytes32 value3, bytes32 value4,
bytes32 value5, bytes32 value6, bytes32 value7, bytes32 value8
) internal pure returns(bytes32 hash) {
assembly ("memory-safe") {
let ptrStart := ptr
let length := mload(ptrStart)
ptr := add(ptrStart, length)
length := add(length, 0x100)
mstore(add(ptr, 0x20), value1)
mstore(add(ptr, 0x40), value2)
mstore(add(ptr, 0x60), value3)
mstore(add(ptr, 0x80), value4)
mstore(add(ptr, 0xA0), value5)
mstore(add(ptr, 0xC0), value6)
mstore(add(ptr, 0xE0), value7)
mstore(add(ptr, 0x100), value8)
hash := keccak256(add(ptrStart, 0x20), length)
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {EfficientHash} from "../src/EfficientHash.sol";
contract EfficientHashTest is Test {
function setUp() public {
}
function testFuzzEfficientHash(bytes32 someRandomValue) public {
for (uint256 i = 1; i < 100; ++i) {
bytes32[] memory valuesToHash = new bytes32[](i);
for (uint256 j; j < i; ++j) {
if (j == 0) valuesToHash[j] = EfficientHash.efficientHash(someRandomValue);
else valuesToHash[j] = EfficientHash.efficientHash(valuesToHash[j-1]);
}
bytes32 efficientHashHash;
bytes32 abiEncodeHash;
uint256 efficientHashGas;
uint256 abiEncodeGas;
if (valuesToHash.length == 1) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHash(valuesToHash[0]);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(abi.encode(valuesToHash[0]));
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 2) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHash(
valuesToHash[0],
valuesToHash[1]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 3) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHash(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 4) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHash(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 5) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHash(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 6) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHash(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 7) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHash(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 8) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHash(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 9) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashNineStep2(
EfficientHash.efficientHashNineStep1(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 10) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashTenStep2(
EfficientHash.efficientHashTenStep1(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8],
valuesToHash[9]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 11) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashElevenStep2(
EfficientHash.efficientHashElevenStep1(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9],
valuesToHash[10]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8],
valuesToHash[9],
valuesToHash[10]
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 12) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashTwelveStep2(
EfficientHash.efficientHashTwelveStep1(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9],
valuesToHash[10],
valuesToHash[11]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
bytes.concat(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8]
),
abi.encode(
valuesToHash[9],
valuesToHash[10],
valuesToHash[11]
)
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 13) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashThirteenStep2(
EfficientHash.efficientHashThirteenStep1(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
bytes.concat(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8]
),
abi.encode(
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12]
)
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 14) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashFourteenStep2(
EfficientHash.efficientHashFourteenStep1(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
bytes.concat(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8]
),
abi.encode(
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13]
)
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 15) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashFifteenStep2(
EfficientHash.efficientHashFifteenStep1(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13],
valuesToHash[14]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
bytes.concat(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8]
),
abi.encode(
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13],
valuesToHash[14]
)
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 16) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashSixteenStep2(
EfficientHash.efficientHashSixteenStep1(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13],
valuesToHash[14],
valuesToHash[15]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
bytes.concat(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8]
),
abi.encode(
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13],
valuesToHash[14],
valuesToHash[15]
)
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length == 23) {
efficientHashGas = gasleft();
efficientHashHash = EfficientHash.efficientHashExtensionEnd(
EfficientHash.efficientHashExtensionContinue(
EfficientHash.efficientHashExtensionStart(
valuesToHash.length,
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13],
valuesToHash[14],
valuesToHash[15]
),
valuesToHash[16],
valuesToHash[17],
valuesToHash[18],
valuesToHash[19],
valuesToHash[20],
valuesToHash[21],
valuesToHash[22]
);
efficientHashGas -= gasleft();
abiEncodeGas = gasleft();
abiEncodeHash = keccak256(
bytes.concat(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
valuesToHash[5],
valuesToHash[6],
valuesToHash[7],
valuesToHash[8]
),
abi.encode(
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13],
valuesToHash[14],
valuesToHash[15],
valuesToHash[16],
valuesToHash[17]
),
abi.encode(
valuesToHash[18],
valuesToHash[19],
valuesToHash[20],
valuesToHash[21],
valuesToHash[22]
)
)
);
abiEncodeGas -= gasleft();
} else if (valuesToHash.length > 16) {
efficientHashGas = gasleft();
uint256 ptr;
(ptr, efficientHashHash) = _efficientExtension(valuesToHash, someRandomValue);
efficientHashGas -= gasleft();
/*for (uint256 x = 0;x < valuesToHash.length;++x) {
bytes32 valueAtX;
assembly {
valueAtX := mload(add(add(ptr, 0x20), mul(x, 0x20)))
}
console.logBytes32(valueAtX);
}*/
abiEncodeGas = gasleft();
abiEncodeHash = _abiEncodeExtension(valuesToHash, someRandomValue);
abiEncodeGas -= gasleft();
}
assertEq(efficientHashHash, abiEncodeHash);
assertTrue(efficientHashGas < abiEncodeGas);
console.log("Gas Savings (%s): %s", valuesToHash.length, (abiEncodeGas - efficientHashGas));
}
}
function _efficientExtension(bytes32[] memory valuesToHash, bytes32 someRandomValue) internal view returns(uint256 ptr, bytes32 efficientHashHash) {
ptr = EfficientHash.efficientHashExtensionContinue(
EfficientHash.efficientHashExtensionStart(
valuesToHash.length,
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
someRandomValue,
valuesToHash[6],
valuesToHash[7]
),
valuesToHash[8],
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13],
valuesToHash[14],
valuesToHash[15]
);
uint256 index = 16;
while (index + 8 < valuesToHash.length) {
EfficientHash.efficientHashExtensionContinue(
ptr,
valuesToHash[index],
valuesToHash[index+1],
valuesToHash[index+2],
valuesToHash[index+3],
valuesToHash[index+4],
valuesToHash[index+5],
valuesToHash[index+6],
valuesToHash[index+7]
);
index += 8;
}
uint256 remaining = valuesToHash.length - index;
if (remaining == 1) {
efficientHashHash = EfficientHash.efficientHashExtensionEnd(ptr, valuesToHash[index]);
} else if (remaining == 2) {
efficientHashHash = EfficientHash.efficientHashExtensionEnd(
ptr,
valuesToHash[index],
valuesToHash[index+1]
);
} else if (remaining == 3) {
efficientHashHash = EfficientHash.efficientHashExtensionEnd(
ptr,
valuesToHash[index],
valuesToHash[index+1],
valuesToHash[index+2]
);
} else if (remaining == 4) {
efficientHashHash = EfficientHash.efficientHashExtensionEnd(
ptr,
valuesToHash[index],
valuesToHash[index+1],
valuesToHash[index+2],
valuesToHash[index+3]
);
} else if (remaining == 5) {
efficientHashHash = EfficientHash.efficientHashExtensionEnd(
ptr,
valuesToHash[index],
valuesToHash[index+1],
valuesToHash[index+2],
valuesToHash[index+3],
valuesToHash[index+4]
);
} else if (remaining == 6) {
efficientHashHash = EfficientHash.efficientHashExtensionEnd(
ptr,
valuesToHash[index],
valuesToHash[index+1],
valuesToHash[index+2],
valuesToHash[index+3],
valuesToHash[index+4],
valuesToHash[index+5]
);
} else if (remaining == 7) {
efficientHashHash = EfficientHash.efficientHashExtensionEnd(
ptr,
valuesToHash[index],
valuesToHash[index+1],
valuesToHash[index+2],
valuesToHash[index+3],
valuesToHash[index+4],
valuesToHash[index+5],
valuesToHash[index+6]
);
} else if (remaining == 8) {
efficientHashHash = EfficientHash.efficientHashExtensionEnd(
ptr,
valuesToHash[index],
valuesToHash[index+1],
valuesToHash[index+2],
valuesToHash[index+3],
valuesToHash[index+4],
valuesToHash[index+5],
valuesToHash[index+6],
valuesToHash[index+7]
);
}
}
function _abiEncodeExtension(bytes32[] memory valuesToHash, bytes32 someRandomValue) internal view returns (bytes32 abiEncodeHash) {
bytes memory values =
bytes.concat(
abi.encode(
valuesToHash[0],
valuesToHash[1],
valuesToHash[2],
valuesToHash[3],
valuesToHash[4],
someRandomValue,
valuesToHash[6],
valuesToHash[7],
valuesToHash[8]
),
abi.encode(
valuesToHash[9],
valuesToHash[10],
valuesToHash[11],
valuesToHash[12],
valuesToHash[13],
valuesToHash[14],
valuesToHash[15]
)
);
uint256 indexAbi = 16;
while (indexAbi + 8 < valuesToHash.length) {
values = bytes.concat(
values,
abi.encode(
valuesToHash[indexAbi],
valuesToHash[indexAbi+1],
valuesToHash[indexAbi+2],
valuesToHash[indexAbi+3],
valuesToHash[indexAbi+4],
valuesToHash[indexAbi+5],
valuesToHash[indexAbi+6],
valuesToHash[indexAbi+7]
)
);
indexAbi += 8;
}
uint256 remainingAbi = valuesToHash.length - indexAbi;
if (remainingAbi == 1) {
values = bytes.concat(
values,
valuesToHash[indexAbi]
);
abiEncodeHash = keccak256(values);
} else if (remainingAbi == 2) {
values = bytes.concat(
values,
abi.encode(
valuesToHash[indexAbi],
valuesToHash[indexAbi+1]
)
);
abiEncodeHash = keccak256(values);
} else if (remainingAbi == 3) {
values = bytes.concat(
values,
abi.encode(
valuesToHash[indexAbi],
valuesToHash[indexAbi+1],
valuesToHash[indexAbi+2]
)
);
abiEncodeHash = keccak256(values);
} else if (remainingAbi == 4) {
values = bytes.concat(
values,
abi.encode(
valuesToHash[indexAbi],
valuesToHash[indexAbi+1],
valuesToHash[indexAbi+2],
valuesToHash[indexAbi+3]
)
);
abiEncodeHash = keccak256(values);
} else if (remainingAbi == 5) {
values = bytes.concat(
values,
abi.encode(
valuesToHash[indexAbi],
valuesToHash[indexAbi+1],
valuesToHash[indexAbi+2],
valuesToHash[indexAbi+3],
valuesToHash[indexAbi+4]
)
);
abiEncodeHash = keccak256(values);
} else if (remainingAbi == 6) {
values = bytes.concat(
values,
abi.encode(
valuesToHash[indexAbi],
valuesToHash[indexAbi+1],
valuesToHash[indexAbi+2],
valuesToHash[indexAbi+3],
valuesToHash[indexAbi+4],
valuesToHash[indexAbi+5]
)
);
abiEncodeHash = keccak256(values);
} else if (remainingAbi == 7) {
values = bytes.concat(
values,
abi.encode(
valuesToHash[indexAbi],
valuesToHash[indexAbi+1],
valuesToHash[indexAbi+2],
valuesToHash[indexAbi+3],
valuesToHash[indexAbi+4],
valuesToHash[indexAbi+5],
valuesToHash[indexAbi+6]
)
);
abiEncodeHash = keccak256(values);
} else if (remainingAbi == 8) {
values = bytes.concat(
values,
abi.encode(
valuesToHash[indexAbi],
valuesToHash[indexAbi+1],
valuesToHash[indexAbi+2],
valuesToHash[indexAbi+3],
valuesToHash[indexAbi+4],
valuesToHash[indexAbi+5],
valuesToHash[indexAbi+6],
valuesToHash[indexAbi+7]
)
);
abiEncodeHash = keccak256(values);
}
//console.logBytes(values);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment