Skip to content
This repository has been archived by the owner on Mar 25, 2024. It is now read-only.

Commit

Permalink
Merge pull request #6 from matter-labs/vb-keccak-precompile
Browse files Browse the repository at this point in the history
keccak precompile
  • Loading branch information
vladbochok authored Apr 14, 2022
2 parents 0d0a627 + 56775bd commit 37dc819
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 25 deletions.
78 changes: 78 additions & 0 deletions contracts/Keccak256.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import './SystemContractHelper.sol';

/**
* @author Matter Labs
*/
contract Keccak256 {
uint256 constant PERMANENT_ERGS_COST = 100;
uint256 constant INTERNAL_KECCAK_ROUND_ERGS_COST = 100;
uint256 constant MAX_PREIMAGE_BYTES_LENGTH = 1024;
uint256 constant BLOCK_SIZE = 136;
uint32 constant INPUT_OFFSET_IN_WORDS = 0;
uint32 constant OUTPUT_OFFSET_IN_WORDS = 0;
uint32 constant OUTPUT_LENGTH_IN_WORDS = 1;

fallback() external {
address codeAddress = SystemContractHelper.getCodeAddress();
// Check that we are NOT in delegatecall
require(codeAddress == address(this));

uint256 bytesSize;

assembly {
bytesSize := calldatasize()
}

require(bytesSize <= MAX_PREIMAGE_BYTES_LENGTH);

uint256 padLen = BLOCK_SIZE - bytesSize % BLOCK_SIZE;
uint256 paddedByteSize = bytesSize + padLen;
assert(paddedByteSize % BLOCK_SIZE == 0); // can deleted later one
uint64 numRounds = uint64(paddedByteSize / BLOCK_SIZE);

// manual memory copy and management, as we do not care about Solidity allocations
uint32 inputLengthInWords = uint32(paddedByteSize / 32);
if (paddedByteSize % 32 != 0) {
unchecked {
inputLengthInWords += 1;
}
}


assembly {
calldatacopy(0x00, 0x00, bytesSize)
}

if (padLen == 1) {
// write 0x81 at the end
assembly {
mstore(add(bytesSize, 1), 0x8100000000000000000000000000000000000000000000000000000000000000) // we do not care about what is after
}
} else {
assembly {
mstore(add(bytesSize, 1), 0x0100000000000000000000000000000000000000000000000000000000000000)
mstore(sub(paddedByteSize, 1), 0x8000000000000000000000000000000000000000000000000000000000000000)
}
}

uint256 precompileParams = SystemContractHelper.packPrecompileParams(
INPUT_OFFSET_IN_WORDS,
inputLengthInWords,
OUTPUT_OFFSET_IN_WORDS,
OUTPUT_LENGTH_IN_WORDS,
numRounds
);

uint256 ergsToPay = PERMANENT_ERGS_COST + INTERNAL_KECCAK_ROUND_ERGS_COST * uint256(numRounds);
bool success = SystemContractHelper.precompileCall(precompileParams, uint32(ergsToPay));
require(success);

assembly {
return(0, 32)
}
}
}
46 changes: 28 additions & 18 deletions contracts/SystemContractHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,34 @@ library SystemContractHelper {
}
}

// Call precompile with given parameters.
// The list of currently available precompiles sha256, keccak256, ecrecover, deployer.
// NOTE: The precompile type depends on `this` which calls precompile, which means that only
// system contracts corresponding to the list of precompiles above can do `precompileCall`.
function precompileCall(PrecompileCall memory _params, uint32 _ergsToBurn) internal view {
address callAddr = PRECOMPILE_CALL_ADDRESS;
uint256 rawParams = _params.inputMemoryOffset;
rawParams |= uint256(_params.inputMemoryLength) << 32;
rawParams |= uint256(_params.outputMemoryOffset) << 64;
rawParams |= uint256(_params.outputMemoryLength) << 96;

// After `precompileCall` ergs will be burned down to 0 if there are not enough of them,
// thats why it should be checked before the call.
require(gasleft() >= _ergsToBurn);
assembly {
let success := staticcall(rawParams, callAddr, _ergsToBurn, 0, 0, 0)
}
}
function packPrecompileParams(
uint32 inputMemoryOffset,
uint32 inputMemoryLength,
uint32 outputMemoryOffset,
uint32 outputMemoryLength,
uint64 perPrecompileInterpreted
) external pure returns (uint256 rawParams) {
rawParams = inputMemoryOffset;
rawParams |= uint256(inputMemoryLength) << 32;
rawParams |= uint256(outputMemoryOffset) << 64;
rawParams |= uint256(outputMemoryLength) << 96;
rawParams |= uint256(perPrecompileInterpreted) << 192;
}

// Call precompile with given parameters.
// The list of currently available precompiles sha256, keccak256, ecrecover, deployer.
// NOTE: The precompile type depends on `this` which calls precompile, which means that only
// system contracts corresponding to the list of precompiles above can do `precompileCall`.
function precompileCall(uint256 _rawParams, uint32 _ergsToBurn) internal view returns (bool success) {
address callAddr = PRECOMPILE_CALL_ADDRESS;

// After `precompileCall` ergs will be burned down to 0 if there are not enough of them,
// thats why it should be checked before the call.
require(gasleft() >= _ergsToBurn);
assembly {
success := staticcall(_rawParams, callAddr, _ergsToBurn, 0, 0, 0)
}
}

// Allows to perform a call with a custom `msg.sender`.
function mimicCall(
Expand Down
7 changes: 0 additions & 7 deletions contracts/interfaces/IKeccak256.sol

This file was deleted.

0 comments on commit 37dc819

Please sign in to comment.