Skip to content

Commit 82aabed

Browse files
jameswenzelGitHub Actions Bot
authored and
GitHub Actions Bot
committed
initial random bytes and eip1271 offerer
1 parent 86a319a commit 82aabed

File tree

4 files changed

+200
-0
lines changed

4 files changed

+200
-0
lines changed

scripts/find_optimizer_runs.sh

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/bin/bash
2+
# h/t @DrakeEvansV1 & ChatGPT
3+
4+
TARGET_SIZE=24.576
5+
MIN_RUNS=1
6+
# NOTE that at time of writing, Etherscan does not support verifying contracts
7+
# that specify more than 10,000,000 optimizer runs.
8+
# Higher numbers do not always result in different bytecode output. If a
9+
# higher number of runs is used, it may be possible verify by spoofing with a
10+
# number that results in the same bytecode output. This is not guaranteed.
11+
MAX_RUNS=$((2**32-1))
12+
ENV_FILE=".env"
13+
FOUND_RUNS=0
14+
15+
# Check if the optimizer is enabled
16+
OPTIMIZER_STATUS=$(forge config | grep optimizer | head -n 1)
17+
if [ "$OPTIMIZER_STATUS" != "optimizer = true" ]; then
18+
echo "Error: The optimizer is not enabled. Please enable it and try again."
19+
exit 1
20+
fi
21+
22+
try_runs() {
23+
local RUNS=$1
24+
printf "Trying with FOUNDRY_OPTIMIZER_RUNS=%d\n" "$RUNS"
25+
RESULT=$(FOUNDRY_OPTIMIZER_RUNS=$RUNS forge build --sizes | grep Seaport | head -n 1)
26+
CONTRACT_SIZE=$(echo $RESULT | awk -F'|' '{print $3}' | awk '{print $1}')
27+
[ "$(echo "$CONTRACT_SIZE<=$TARGET_SIZE" | bc)" -eq 1 ]
28+
}
29+
30+
if try_runs $MAX_RUNS; then
31+
FOUND_RUNS=$MAX_RUNS
32+
else
33+
while [ $MIN_RUNS -le $MAX_RUNS ]; do
34+
MID_RUNS=$(( (MIN_RUNS + MAX_RUNS) / 2 ))
35+
36+
if try_runs $MID_RUNS; then
37+
printf "Success with FOUNDRY_OPTIMIZER_RUNS=%d and contract size %.3fKB\n" "$MID_RUNS" "$CONTRACT_SIZE"
38+
MIN_RUNS=$((MID_RUNS + 1))
39+
FOUND_RUNS=$MID_RUNS
40+
else
41+
printf "Failure with FOUNDRY_OPTIMIZER_RUNS=%d and contract size %.3fKB\n" "$MID_RUNS" "$CONTRACT_SIZE"
42+
MAX_RUNS=$((MID_RUNS - 1))
43+
fi
44+
done
45+
fi
46+
47+
printf "Highest FOUNDRY_OPTIMIZER_RUNS found: %d\n" "$FOUND_RUNS"
48+
49+
if [ -f "$ENV_FILE" ]; then
50+
if grep -q "^FOUNDRY_OPTIMIZER_RUNS=" "$ENV_FILE"; then
51+
awk -v runs="$FOUND_RUNS" '{gsub(/^FOUNDRY_OPTIMIZER_RUNS=.*/, "FOUNDRY_OPTIMIZER_RUNS="runs); print}' "$ENV_FILE" > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE"
52+
else
53+
echo "FOUNDRY_OPTIMIZER_RUNS=$FOUND_RUNS" >> "$ENV_FILE"
54+
fi
55+
printf "Updated %s with FOUNDRY_OPTIMIZER_RUNS=%d\n" "$ENV_FILE" "$FOUND_RUNS"
56+
else
57+
printf "Error: %s not found.\n" "$ENV_FILE"
58+
fi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.13;
3+
4+
contract EIP1271Offerer {
5+
error InvalidSignature(bytes32 digest, bytes signature);
6+
bytes4 private constant _EIP_1271_MAGIC_VALUE = 0x1626ba7e;
7+
8+
mapping(bytes32 => bytes32) public digestToSignatureHash;
9+
10+
function registerSignature(bytes32 digest, bytes memory signature) public {
11+
digestToSignatureHash[digest] = keccak256(signature);
12+
}
13+
14+
function isValidSignature(
15+
bytes32 digest,
16+
bytes memory signature
17+
) external view returns (bytes4) {
18+
bytes32 signatureHash = keccak256(signature);
19+
if (digestToSignatureHash[digest] == signatureHash) {
20+
return _EIP_1271_MAGIC_VALUE;
21+
}
22+
revert InvalidSignature(digest, signature);
23+
}
24+
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.17;
3+
import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
4+
5+
library RandomBytes {
6+
uint256 constant MAX_RANDOM_LENGTH = 1000;
7+
8+
function randomBytes(
9+
LibPRNG.PRNG memory prng,
10+
uint256 length
11+
) internal pure returns (bytes memory) {
12+
bytes memory bytesArray = new bytes(length);
13+
// track the number of bytes we've written
14+
uint256 i;
15+
// loop until we've written `length` bytes
16+
while (i < length) {
17+
// get a random chunk of 32 bytes
18+
bytes32 randomChunk = bytes32(LibPRNG.next(prng));
19+
// loop through the chunk and write each byte to the output
20+
// stop if we've written `length` bytes
21+
for (uint256 j; j < 32 && i < length; ) {
22+
bytesArray[i] = randomChunk[j];
23+
unchecked {
24+
// increment both counters
25+
++i;
26+
++j;
27+
}
28+
}
29+
}
30+
return bytesArray;
31+
}
32+
33+
function randomBytes(
34+
LibPRNG.PRNG memory prng
35+
) internal pure returns (bytes memory) {
36+
// get a random length between 1 and MAX_RANDOM_LENGTH
37+
uint256 length = (LibPRNG.next(prng) % MAX_RANDOM_LENGTH) + 1;
38+
return randomBytes(prng, length);
39+
}
40+
}
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.17;
3+
4+
import { Test } from "forge-std/Test.sol";
5+
import { RandomBytes } from "./RandomBytes.sol";
6+
import { LibPRNG } from "solady/src/utils/LibPRNG.sol";
7+
8+
contract RandomBytesTest is Test {
9+
function testRandomBytes() public {
10+
LibPRNG.PRNG memory prng = LibPRNG.PRNG(0);
11+
bytes memory bytesArray = RandomBytes.randomBytes(prng, 100);
12+
assertEq(bytesArray.length, 100, "randomBytes length");
13+
// assert not more than 5 0 bytes in a row
14+
// this will fail 1/2^40 times
15+
uint256 zeroCount = 0;
16+
for (uint256 i = 0; i < bytesArray.length; i++) {
17+
if (bytesArray[i] == 0) {
18+
zeroCount++;
19+
} else {
20+
zeroCount = 0;
21+
}
22+
assertLt(zeroCount, 6, "randomBytes zero count");
23+
}
24+
}
25+
26+
function testRandomBytes(uint256 seed) public {
27+
LibPRNG.PRNG memory prng = LibPRNG.PRNG(seed);
28+
bytes memory bytesArray = RandomBytes.randomBytes(prng, 100);
29+
assertEq(bytesArray.length, 100, "randomBytes length");
30+
// assert not more than 5 0 bytes in a row
31+
// this will fail 1/2^40 times :(
32+
uint256 zeroCount = 0;
33+
for (uint256 i = 0; i < bytesArray.length; i++) {
34+
if (bytesArray[i] == 0) {
35+
zeroCount++;
36+
} else {
37+
zeroCount = 0;
38+
}
39+
assertLt(zeroCount, 6, "randomBytes zero count");
40+
}
41+
}
42+
43+
function testRandomBytesRandomLength() public {
44+
LibPRNG.PRNG memory prng = LibPRNG.PRNG(0);
45+
bytes memory bytesArray = RandomBytes.randomBytes(prng);
46+
assertLt(bytesArray.length, 1001, "randomBytes length");
47+
assertGt(bytesArray.length, 0, "randomBytes length");
48+
// assert not more than 5 0 bytes in a row
49+
// this will fail 1/2^40 times
50+
uint256 zeroCount = 0;
51+
for (uint256 i = 0; i < bytesArray.length; i++) {
52+
if (bytesArray[i] == 0) {
53+
zeroCount++;
54+
} else {
55+
zeroCount = 0;
56+
}
57+
assertLt(zeroCount, 6, "randomBytes zero count");
58+
}
59+
}
60+
61+
function testRandomBytesRandomLength(uint256 seed) public {
62+
LibPRNG.PRNG memory prng = LibPRNG.PRNG(seed);
63+
bytes memory bytesArray = RandomBytes.randomBytes(prng);
64+
assertLt(bytesArray.length, 1001, "randomBytes length");
65+
assertGt(bytesArray.length, 0, "randomBytes length");
66+
// assert not more than 5 0 bytes in a row
67+
// this will fail 1/2^40 times
68+
uint256 zeroCount = 0;
69+
for (uint256 i = 0; i < bytesArray.length; i++) {
70+
if (bytesArray[i] == 0) {
71+
zeroCount++;
72+
} else {
73+
zeroCount = 0;
74+
}
75+
assertLt(zeroCount, 6, "randomBytes zero count");
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)