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

Commit 37dc819

Browse files
authored
Merge pull request #6 from matter-labs/vb-keccak-precompile
keccak precompile
2 parents 0d0a627 + 56775bd commit 37dc819

File tree

3 files changed

+106
-25
lines changed

3 files changed

+106
-25
lines changed

contracts/Keccak256.sol

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
pragma solidity ^0.8.0;
4+
5+
import './SystemContractHelper.sol';
6+
7+
/**
8+
* @author Matter Labs
9+
*/
10+
contract Keccak256 {
11+
uint256 constant PERMANENT_ERGS_COST = 100;
12+
uint256 constant INTERNAL_KECCAK_ROUND_ERGS_COST = 100;
13+
uint256 constant MAX_PREIMAGE_BYTES_LENGTH = 1024;
14+
uint256 constant BLOCK_SIZE = 136;
15+
uint32 constant INPUT_OFFSET_IN_WORDS = 0;
16+
uint32 constant OUTPUT_OFFSET_IN_WORDS = 0;
17+
uint32 constant OUTPUT_LENGTH_IN_WORDS = 1;
18+
19+
fallback() external {
20+
address codeAddress = SystemContractHelper.getCodeAddress();
21+
// Check that we are NOT in delegatecall
22+
require(codeAddress == address(this));
23+
24+
uint256 bytesSize;
25+
26+
assembly {
27+
bytesSize := calldatasize()
28+
}
29+
30+
require(bytesSize <= MAX_PREIMAGE_BYTES_LENGTH);
31+
32+
uint256 padLen = BLOCK_SIZE - bytesSize % BLOCK_SIZE;
33+
uint256 paddedByteSize = bytesSize + padLen;
34+
assert(paddedByteSize % BLOCK_SIZE == 0); // can deleted later one
35+
uint64 numRounds = uint64(paddedByteSize / BLOCK_SIZE);
36+
37+
// manual memory copy and management, as we do not care about Solidity allocations
38+
uint32 inputLengthInWords = uint32(paddedByteSize / 32);
39+
if (paddedByteSize % 32 != 0) {
40+
unchecked {
41+
inputLengthInWords += 1;
42+
}
43+
}
44+
45+
46+
assembly {
47+
calldatacopy(0x00, 0x00, bytesSize)
48+
}
49+
50+
if (padLen == 1) {
51+
// write 0x81 at the end
52+
assembly {
53+
mstore(add(bytesSize, 1), 0x8100000000000000000000000000000000000000000000000000000000000000) // we do not care about what is after
54+
}
55+
} else {
56+
assembly {
57+
mstore(add(bytesSize, 1), 0x0100000000000000000000000000000000000000000000000000000000000000)
58+
mstore(sub(paddedByteSize, 1), 0x8000000000000000000000000000000000000000000000000000000000000000)
59+
}
60+
}
61+
62+
uint256 precompileParams = SystemContractHelper.packPrecompileParams(
63+
INPUT_OFFSET_IN_WORDS,
64+
inputLengthInWords,
65+
OUTPUT_OFFSET_IN_WORDS,
66+
OUTPUT_LENGTH_IN_WORDS,
67+
numRounds
68+
);
69+
70+
uint256 ergsToPay = PERMANENT_ERGS_COST + INTERNAL_KECCAK_ROUND_ERGS_COST * uint256(numRounds);
71+
bool success = SystemContractHelper.precompileCall(precompileParams, uint32(ergsToPay));
72+
require(success);
73+
74+
assembly {
75+
return(0, 32)
76+
}
77+
}
78+
}

contracts/SystemContractHelper.sol

+28-18
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,34 @@ library SystemContractHelper {
5858
}
5959
}
6060

61-
// Call precompile with given parameters.
62-
// The list of currently available precompiles sha256, keccak256, ecrecover, deployer.
63-
// NOTE: The precompile type depends on `this` which calls precompile, which means that only
64-
// system contracts corresponding to the list of precompiles above can do `precompileCall`.
65-
function precompileCall(PrecompileCall memory _params, uint32 _ergsToBurn) internal view {
66-
address callAddr = PRECOMPILE_CALL_ADDRESS;
67-
uint256 rawParams = _params.inputMemoryOffset;
68-
rawParams |= uint256(_params.inputMemoryLength) << 32;
69-
rawParams |= uint256(_params.outputMemoryOffset) << 64;
70-
rawParams |= uint256(_params.outputMemoryLength) << 96;
71-
72-
// After `precompileCall` ergs will be burned down to 0 if there are not enough of them,
73-
// thats why it should be checked before the call.
74-
require(gasleft() >= _ergsToBurn);
75-
assembly {
76-
let success := staticcall(rawParams, callAddr, _ergsToBurn, 0, 0, 0)
77-
}
78-
}
61+
function packPrecompileParams(
62+
uint32 inputMemoryOffset,
63+
uint32 inputMemoryLength,
64+
uint32 outputMemoryOffset,
65+
uint32 outputMemoryLength,
66+
uint64 perPrecompileInterpreted
67+
) external pure returns (uint256 rawParams) {
68+
rawParams = inputMemoryOffset;
69+
rawParams |= uint256(inputMemoryLength) << 32;
70+
rawParams |= uint256(outputMemoryOffset) << 64;
71+
rawParams |= uint256(outputMemoryLength) << 96;
72+
rawParams |= uint256(perPrecompileInterpreted) << 192;
73+
}
74+
75+
// Call precompile with given parameters.
76+
// The list of currently available precompiles sha256, keccak256, ecrecover, deployer.
77+
// NOTE: The precompile type depends on `this` which calls precompile, which means that only
78+
// system contracts corresponding to the list of precompiles above can do `precompileCall`.
79+
function precompileCall(uint256 _rawParams, uint32 _ergsToBurn) internal view returns (bool success) {
80+
address callAddr = PRECOMPILE_CALL_ADDRESS;
81+
82+
// After `precompileCall` ergs will be burned down to 0 if there are not enough of them,
83+
// thats why it should be checked before the call.
84+
require(gasleft() >= _ergsToBurn);
85+
assembly {
86+
success := staticcall(_rawParams, callAddr, _ergsToBurn, 0, 0, 0)
87+
}
88+
}
7989

8090
// Allows to perform a call with a custom `msg.sender`.
8191
function mimicCall(

contracts/interfaces/IKeccak256.sol

-7
This file was deleted.

0 commit comments

Comments
 (0)