@@ -20,6 +20,8 @@ contract BytecodeRepositoryTest is Test {
2020 address public auditor;
2121 address public author;
2222
23+ uint256 public auditorPK = vm.randomUint ();
24+
2325 uint256 public authorPK = vm.randomUint ();
2426
2527 bytes32 private constant _TEST_CONTRACT = "TEST_CONTRACT " ;
@@ -29,16 +31,243 @@ contract BytecodeRepositoryTest is Test {
2931
3032 function setUp () public {
3133 owner = makeAddr ("owner " );
32- auditor = makeAddr ( " auditor " );
34+ auditor = vm. addr (auditorPK );
3335 author = vm.addr (authorPK);
3436
3537 vm.startPrank (owner);
36- repository = new BytecodeRepository (address ( this ) );
38+ repository = new BytecodeRepository (owner );
3739 repository.addAuditor (auditor, "Test Auditor " );
3840 vm.stopPrank ();
3941 }
4042
4143 function _getMockBytecode (bytes32 _contractType , uint256 _version ) internal pure returns (bytes memory ) {
4244 return abi.encodePacked (type (MockedVersionContract).creationCode, abi.encode (_contractType, _version));
4345 }
46+
47+ function _uploadTestBytecode () internal returns (bytes32 bytecodeHash ) {
48+ bytes memory bytecode = _getMockBytecode (_TEST_CONTRACT, _TEST_VERSION);
49+
50+ Bytecode memory bc = Bytecode ({
51+ contractType: _TEST_CONTRACT,
52+ version: _TEST_VERSION,
53+ initCode: bytecode,
54+ author: author,
55+ source: _TEST_SOURCE,
56+ authorSignature: bytes ("" )
57+ });
58+
59+ bytecodeHash = repository.computeBytecodeHash (bc);
60+
61+ (uint8 v , bytes32 r , bytes32 s ) =
62+ vm.sign (authorPK, repository.domainSeparatorV4 ().toTypedDataHash (bytecodeHash));
63+ bc.authorSignature = abi.encodePacked (r, s, v);
64+
65+ vm.prank (author);
66+ repository.uploadBytecode (bc);
67+ }
68+
69+ /// UPLOAD BYTECODE TESTS
70+
71+ function test_BCR_01_uploadBytecode_works () public {
72+ bytes memory bytecode = _getMockBytecode (_TEST_CONTRACT, _TEST_VERSION);
73+
74+ Bytecode memory bc = Bytecode ({
75+ contractType: _TEST_CONTRACT,
76+ version: _TEST_VERSION,
77+ initCode: bytecode,
78+ author: author,
79+ source: _TEST_SOURCE,
80+ authorSignature: bytes ("" )
81+ });
82+
83+ bytes32 bytecodeHash = repository.computeBytecodeHash (bc);
84+
85+ // Sign bytecode hash with author's key
86+ (uint8 v , bytes32 r , bytes32 s ) =
87+ vm.sign (authorPK, repository.domainSeparatorV4 ().toTypedDataHash (bytecodeHash));
88+ bc.authorSignature = abi.encodePacked (r, s, v);
89+
90+ vm.prank (author);
91+ repository.uploadBytecode (bc);
92+
93+ // Verify bytecode was stored
94+ assertTrue (repository.isBytecodeUploaded (bytecodeHash));
95+
96+ Bytecode memory storedBc = repository.bytecodeByHash (bytecodeHash);
97+ assertEq (storedBc.contractType, _TEST_CONTRACT);
98+ assertEq (storedBc.version, _TEST_VERSION);
99+ assertEq (storedBc.author, author);
100+ assertEq (storedBc.source, _TEST_SOURCE);
101+ }
102+
103+ function test_BCR_02_uploadBytecode_reverts_if_already_exists () public {
104+ bytes memory bytecode = _getMockBytecode (_TEST_CONTRACT, _TEST_VERSION);
105+
106+ Bytecode memory bc = Bytecode ({
107+ contractType: _TEST_CONTRACT,
108+ version: _TEST_VERSION,
109+ initCode: bytecode,
110+ author: author,
111+ source: _TEST_SOURCE,
112+ authorSignature: bytes ("" )
113+ });
114+
115+ bytes32 bytecodeHash = repository.computeBytecodeHash (bc);
116+
117+ (uint8 v , bytes32 r , bytes32 s ) =
118+ vm.sign (authorPK, repository.domainSeparatorV4 ().toTypedDataHash (bytecodeHash));
119+ bc.authorSignature = abi.encodePacked (r, s, v);
120+
121+ vm.startPrank (author);
122+ repository.uploadBytecode (bc);
123+
124+ vm.expectRevert (IBytecodeRepository.BytecodeAlreadyExistsException.selector );
125+ repository.uploadBytecode (bc);
126+ vm.stopPrank ();
127+ }
128+
129+ function test_BCR_03_uploadBytecode_reverts_if_invalid_signature () public {
130+ bytes memory bytecode = _getMockBytecode (_TEST_CONTRACT, _TEST_VERSION);
131+
132+ Bytecode memory bc = Bytecode ({
133+ contractType: _TEST_CONTRACT,
134+ version: _TEST_VERSION,
135+ initCode: bytecode,
136+ author: author,
137+ source: _TEST_SOURCE,
138+ authorSignature: bytes ("invalid signature " )
139+ });
140+
141+ vm.prank (author);
142+ vm.expectRevert ("ECDSA: invalid signature length " );
143+ repository.uploadBytecode (bc);
144+ }
145+
146+ /// AUDITOR SIGNATURE TESTS
147+
148+ function test_BCR_04_signBytecodeHash_works () public {
149+ // First upload bytecode
150+ bytes32 bytecodeHash = _uploadTestBytecode ();
151+
152+ // Now sign as auditor
153+ string memory reportUrl = "https://audit.report " ;
154+ bytes32 signatureHash = repository.domainSeparatorV4 ().toTypedDataHash (
155+ keccak256 (abi.encode (repository.AUDITOR_SIGNATURE_TYPEHASH (), bytecodeHash, keccak256 (bytes (reportUrl))))
156+ );
157+
158+ (uint8 v , bytes32 r , bytes32 s ) = vm.sign (auditorPK, signatureHash);
159+ bytes memory signature = abi.encodePacked (r, s, v);
160+
161+ vm.prank (auditor);
162+ repository.signBytecodeHash (bytecodeHash, reportUrl, signature);
163+
164+ // Verify signature was stored
165+ assertTrue (repository.isBytecodeAudited (bytecodeHash));
166+
167+ AuditorSignature[] memory sigs = repository.auditorSignaturesByHash (bytecodeHash);
168+ assertEq (sigs.length , 1 );
169+ assertEq (sigs[0 ].auditor, auditor);
170+ assertEq (sigs[0 ].reportUrl, reportUrl);
171+ assertEq (sigs[0 ].signature, signature);
172+ }
173+
174+ function test_BCR_05_signBytecodeHash_reverts_if_not_auditor () public {
175+ // First upload bytecode
176+ bytes32 bytecodeHash = _uploadTestBytecode ();
177+
178+ // Now sign as auditor
179+ string memory reportUrl = "https://audit.report " ;
180+ bytes32 signatureHash = repository.domainSeparatorV4 ().toTypedDataHash (
181+ keccak256 (abi.encode (repository.AUDITOR_SIGNATURE_TYPEHASH (), bytecodeHash, keccak256 (bytes (reportUrl))))
182+ );
183+
184+ uint256 notAuditorPK = vm.randomUint ();
185+ address notAuditor = vm.addr (notAuditorPK);
186+
187+ (uint8 v , bytes32 r , bytes32 s ) = vm.sign (notAuditorPK, signatureHash);
188+ bytes memory signature = abi.encodePacked (r, s, v);
189+
190+ vm.prank (notAuditor);
191+ vm.expectRevert (abi.encodeWithSelector (IBytecodeRepository.SignerIsNotAuditorException.selector , notAuditor));
192+ repository.signBytecodeHash (bytecodeHash, reportUrl, signature);
193+ }
194+
195+ /// DEPLOYMENT TESTS
196+
197+ function test_BCR_06_deploy_works () public {
198+ // First upload bytecode
199+ bytes32 bytecodeHash = _uploadTestBytecode ();
200+
201+ // Now sign as auditor
202+ string memory reportUrl = "https://audit.report " ;
203+ bytes32 signatureHash = repository.domainSeparatorV4 ().toTypedDataHash (
204+ keccak256 (abi.encode (repository.AUDITOR_SIGNATURE_TYPEHASH (), bytecodeHash, keccak256 (bytes (reportUrl))))
205+ );
206+
207+ (uint8 v , bytes32 r , bytes32 s ) = vm.sign (auditorPK, signatureHash);
208+ bytes memory signature = abi.encodePacked (r, s, v);
209+
210+ vm.prank (auditor);
211+ repository.signBytecodeHash (bytecodeHash, reportUrl, signature);
212+
213+ // Mark as system contract to auto-approve
214+ vm.prank (owner);
215+ repository.allowSystemContract (bytecodeHash);
216+
217+ // Now deploy
218+ address deployer = makeAddr ("deployer " );
219+ vm.prank (deployer);
220+ address deployed = repository.deploy (_TEST_CONTRACT, _TEST_VERSION, "" , _TEST_SALT);
221+
222+ // Verify deployment
223+ assertTrue (deployed.code.length > 0 );
224+ assertEq (repository.deployedContracts (deployed), bytecodeHash);
225+
226+ IVersion version = IVersion (deployed);
227+ assertEq (version.contractType (), _TEST_CONTRACT);
228+ assertEq (version.version (), _TEST_VERSION);
229+ }
230+
231+ function test_BCR_07_deploy_reverts_if_not_approved () public {
232+ vm.expectRevert (
233+ abi.encodeWithSelector (
234+ IBytecodeRepository.BytecodeIsNotApprovedException.selector , _TEST_CONTRACT, _TEST_VERSION
235+ )
236+ );
237+ repository.deploy (_TEST_CONTRACT, _TEST_VERSION, "" , _TEST_SALT);
238+ }
239+
240+ function test_BCR_08_deploy_reverts_if_not_audited () public {
241+ // Upload but don't audit
242+ bytes memory bytecode = _getMockBytecode (_TEST_CONTRACT, _TEST_VERSION);
243+
244+ Bytecode memory bc = Bytecode ({
245+ contractType: _TEST_CONTRACT,
246+ version: _TEST_VERSION,
247+ initCode: bytecode,
248+ author: author,
249+ source: _TEST_SOURCE,
250+ authorSignature: bytes ("" )
251+ });
252+
253+ bytes32 bytecodeHash = repository.computeBytecodeHash (bc);
254+
255+ (uint8 v , bytes32 r , bytes32 s ) =
256+ vm.sign (authorPK, repository.domainSeparatorV4 ().toTypedDataHash (bytecodeHash));
257+ bc.authorSignature = abi.encodePacked (r, s, v);
258+
259+ vm.prank (author);
260+ repository.uploadBytecode (bc);
261+
262+ // Mark as system contract to auto-approve
263+ vm.prank (owner);
264+ repository.allowSystemContract (bytecodeHash);
265+
266+ vm.expectRevert (
267+ abi.encodeWithSelector (
268+ IBytecodeRepository.BytecodeIsNotApprovedException.selector , _TEST_CONTRACT, _TEST_VERSION
269+ )
270+ );
271+ repository.deploy (_TEST_CONTRACT, _TEST_VERSION, "" , _TEST_SALT);
272+ }
44273}
0 commit comments