Skip to content

Commit ef7173b

Browse files
authored
Merge pull request #477 from crytic/fix/compile-libraries-validation
Allow 20 byte address for --compile-libraries and raise error if argument is invalid
2 parents 9ec6702 + 9a0cb86 commit ef7173b

File tree

3 files changed

+75
-4
lines changed

3 files changed

+75
-4
lines changed

crytic_compile/crytic_compile.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,14 @@ def _extract_libraries(libraries_str: Optional[str]) -> Optional[Dict[str, int]]
6363

6464
if not libraries_str:
6565
return None
66-
67-
pattern = r"\((?P<name>\w+),\s*(?P<value1>0x[0-9a-fA-F]{2})\),?"
66+
# Extract tuple like (libname1, 0x00)
67+
pattern = r"\((?P<name>\w+),\s*(?P<value1>0x[0-9a-fA-F]{2,40})\),?"
6868
matches = re.findall(pattern, libraries_str)
6969

7070
if not matches:
71-
logging.info(f"Libraries {libraries_str} could not be parsed")
72-
return None
71+
raise ValueError(
72+
f"Invalid library linking directive\nGot:\n{libraries_str}\nExpected format:\n(libname1, 0x00),(libname2, 0x02)"
73+
)
7374

7475
ret: Dict[str, int] = {}
7576
for key, value in matches:

tests/library_linking.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
library NeedsLinkingA {
2+
function testA() external pure returns (uint) {
3+
return type(uint).max;
4+
}
5+
}
6+
library NeedsLinkingB {
7+
function testB() external pure returns (uint) {
8+
return type(uint).min;
9+
}
10+
}
11+
contract TestLibraryLinking {
12+
function test() external {
13+
NeedsLinkingA.testA();
14+
NeedsLinkingB.testB();
15+
}
16+
}

tests/test_library_linking.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Test library linking
3+
"""
4+
import re
5+
from pathlib import Path
6+
import pytest
7+
from crytic_compile.crytic_compile import CryticCompile
8+
9+
TEST_DIR = Path(__file__).resolve().parent
10+
11+
LIBRARY_PLACEHOLDER_REGEX = r"__.{36}__"
12+
13+
14+
def test_library_linking() -> None:
15+
"""Test that the placeholder is not present in the bytecode when the libraries are provided"""
16+
cc = CryticCompile(
17+
Path(TEST_DIR / "library_linking.sol").as_posix(),
18+
compile_libraries="(NeedsLinkingA, 0xdead),(NeedsLinkingB, 0x000000000000000000000000000000000000beef)",
19+
)
20+
for compilation_unit in cc.compilation_units.values():
21+
for source_unit in compilation_unit.source_units.values():
22+
assert (
23+
len(re.findall(r"__.{36}__", source_unit.bytecode_init("TestLibraryLinking"))) == 2
24+
)
25+
assert (
26+
len(re.findall(r"__.{36}__", source_unit.bytecode_runtime("TestLibraryLinking")))
27+
== 2
28+
)
29+
libraries = compilation_unit.crytic_compile.libraries
30+
assert (
31+
len(
32+
re.findall(
33+
r"__.{36}__", source_unit.bytecode_init("TestLibraryLinking", libraries)
34+
)
35+
)
36+
== 0
37+
)
38+
assert (
39+
len(
40+
re.findall(
41+
r"__.{36}__", source_unit.bytecode_runtime("TestLibraryLinking", libraries)
42+
)
43+
)
44+
== 0
45+
)
46+
47+
48+
def test_library_linking_validation() -> None:
49+
"""Test that invalid compile libraries argument raises an error"""
50+
with pytest.raises(ValueError):
51+
CryticCompile(
52+
Path(TEST_DIR / "library_linking.sol").as_posix(),
53+
compile_libraries="(NeedsLinkingA, 0x)",
54+
)

0 commit comments

Comments
 (0)