-
Notifications
You must be signed in to change notification settings - Fork 42
Contract #894
base: main
Are you sure you want to change the base?
Contract #894
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| // (c) 2024, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
|
|
||
| // SPDX-License-Identifier: LicenseRef-Ecosystem | ||
|
|
||
| pragma solidity 0.8.25; | ||
|
|
||
| import {Test} from "@forge-std/Test.sol"; | ||
| import {ValidatorSet} from "../validator-set.sol"; | ||
|
|
||
| contract ValidatorSetGasTests is Test { | ||
| ValidatorSet internal _validatorSet; | ||
|
|
||
| bytes public constant PUBLIC_KEY = | ||
| hex"123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678"; | ||
|
|
||
| function setUp() public { | ||
| _validatorSet = new ValidatorSet(); | ||
| } | ||
|
|
||
| function testGasUsageRegisterValidator() public { | ||
| uint256 gasStart = gasleft(); | ||
|
|
||
| _validatorSet.registerValidator(1000, PUBLIC_KEY); | ||
|
|
||
| uint256 gasUsed = gasStart - gasleft(); | ||
| emit log_named_uint("Gas used for registration", gasUsed); | ||
| } | ||
|
Check warning on line 28 in contracts/ext-interop/tests/ValidatorSetGasTests.t.sol
|
||
|
|
||
| function testGasUsageGetValidator() public { | ||
| // Register a validator first | ||
| uint256 validatorId = _validatorSet.registerValidator(1000, PUBLIC_KEY); | ||
|
|
||
| uint256 gasStart = gasleft(); | ||
|
|
||
| ValidatorSet.Validator memory validator = _validatorSet.getValidator(validatorId); | ||
|
|
||
| uint256 gasUsed = gasStart - gasleft(); | ||
| emit log_named_uint("Gas used for getting validator", gasUsed); | ||
|
|
||
| // Ensure the data is correct to avoid optimization issues | ||
| assertEq(validator.weight, 1000); | ||
| assertEq(validator.publicKey, PUBLIC_KEY); | ||
| } | ||
|
Check notice on line 44 in contracts/ext-interop/tests/ValidatorSetGasTests.t.sol
|
||
|
|
||
| function testGasUsageUpdateValidator() public { | ||
| // First register a validator | ||
| uint256 validatorId = _validatorSet.registerValidator(1000, PUBLIC_KEY); | ||
|
|
||
| // Create new public key for update (different from original) | ||
| bytes memory newPublicKey = | ||
| hex"fedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcba"; | ||
|
|
||
| uint256 gasStart = gasleft(); | ||
|
|
||
| _validatorSet.updateValidator(validatorId, 2000, newPublicKey); | ||
|
|
||
| uint256 gasUsed = gasStart - gasleft(); | ||
| emit log_named_uint("Gas used for updating validator", gasUsed); | ||
|
|
||
| // Verify the update was successful | ||
| ValidatorSet.Validator memory updatedValidator = _validatorSet.getValidator(validatorId); | ||
| assertEq(updatedValidator.weight, 2000); | ||
| assertEq(updatedValidator.publicKey, newPublicKey); | ||
| } | ||
|
Check notice on line 65 in contracts/ext-interop/tests/ValidatorSetGasTests.t.sol
|
||
|
|
||
| function testGasUsageMultipleRegistrations() public { | ||
| emit log_string("=== Testing gas usage for multiple registrations ==="); | ||
|
|
||
| // Register first validator | ||
| uint256 gasStart1 = gasleft(); | ||
| _validatorSet.registerValidator(1000, PUBLIC_KEY); | ||
| uint256 gasUsed1 = gasStart1 - gasleft(); | ||
|
|
||
| // Register second validator | ||
| uint256 gasStart2 = gasleft(); | ||
| _validatorSet.registerValidator(2000, PUBLIC_KEY); | ||
| uint256 gasUsed2 = gasStart2 - gasleft(); | ||
|
|
||
| // Register third validator | ||
| uint256 gasStart3 = gasleft(); | ||
| _validatorSet.registerValidator(3000, PUBLIC_KEY); | ||
| uint256 gasUsed3 = gasStart3 - gasleft(); | ||
|
|
||
| emit log_named_uint("Gas used for 1st registration", gasUsed1); | ||
| emit log_named_uint("Gas used for 2nd registration", gasUsed2); | ||
| emit log_named_uint("Gas used for 3rd registration", gasUsed3); | ||
|
|
||
| // Check if gas usage increases with each registration (due to storage costs) | ||
| assertTrue(gasUsed1 > 0); | ||
| assertTrue(gasUsed2 > 0); | ||
| assertTrue(gasUsed3 > 0); | ||
| } | ||
|
Check warning on line 93 in contracts/ext-interop/tests/ValidatorSetGasTests.t.sol
|
||
|
|
||
| function testGasUsageComparisonDifferentStakeAmounts() public { | ||
| emit log_string("=== Testing gas usage for different stake amounts ==="); | ||
|
|
||
| // Small stake amount | ||
| uint256 gasStart1 = gasleft(); | ||
| _validatorSet.registerValidator(1, PUBLIC_KEY); | ||
| uint256 gasUsed1 = gasStart1 - gasleft(); | ||
|
|
||
| // Large stake amount | ||
| uint256 gasStart2 = gasleft(); | ||
| _validatorSet.registerValidator(type(uint256).max, PUBLIC_KEY); | ||
| uint256 gasUsed2 = gasStart2 - gasleft(); | ||
|
|
||
| emit log_named_uint("Gas used for small stake (1 wei)", gasUsed1); | ||
| emit log_named_uint("Gas used for large stake (max uint256)", gasUsed2); | ||
|
|
||
| // Gas usage should be similar regardless of stake amount since it's just a uint256 | ||
| uint256 gasUsageDifference = gasUsed2 > gasUsed1 ? gasUsed2 - gasUsed1 : gasUsed1 - gasUsed2; | ||
| emit log_named_uint("Gas usage difference", gasUsageDifference); | ||
|
|
||
| // The difference should be reasonable (less than 30000 gas due to array storage initialization) | ||
| assertTrue( | ||
| gasUsageDifference < 30000, "Gas usage should not vary significantly with stake amount" | ||
| ); | ||
| } | ||
|
Check warning on line 119 in contracts/ext-interop/tests/ValidatorSetGasTests.t.sol
|
||
|
|
||
| function testGasUsagePublicKeySameLength() public { | ||
| emit log_string("=== Testing gas usage for public keys of same length ==="); | ||
|
|
||
| // Short public key | ||
| uint256 gasStart1 = gasleft(); | ||
| _validatorSet.registerValidator(1000, PUBLIC_KEY); | ||
| uint256 gasUsed1 = gasStart1 - gasleft(); | ||
|
|
||
| // Medium public key | ||
| uint256 gasStart2 = gasleft(); | ||
| _validatorSet.registerValidator(1000, PUBLIC_KEY); | ||
| uint256 gasUsed2 = gasStart2 - gasleft(); | ||
|
|
||
| // Long public key | ||
| uint256 gasStart3 = gasleft(); | ||
| _validatorSet.registerValidator(1000, PUBLIC_KEY); | ||
| uint256 gasUsed3 = gasStart3 - gasleft(); | ||
|
|
||
| emit log_named_uint("Gas used for short public key (48 bytes)", gasUsed1); | ||
| emit log_named_uint("Gas used for medium public key (48 bytes)", gasUsed2); | ||
| emit log_named_uint("Gas used for long public key (48 bytes)", gasUsed3); | ||
|
|
||
| // All public keys are now 48 bytes, so gas usage should be similar | ||
| assertTrue(gasUsed1 > 0, "Gas should be used for registration"); | ||
| assertTrue(gasUsed2 > 0, "Gas should be used for registration"); | ||
| assertTrue(gasUsed3 > 0, "Gas should be used for registration"); | ||
|
|
||
| // Gas usage should be similar since all keys are the same length | ||
| uint256 maxGas = gasUsed1 > gasUsed2 | ||
| ? (gasUsed1 > gasUsed3 ? gasUsed1 : gasUsed3) | ||
| : (gasUsed2 > gasUsed3 ? gasUsed2 : gasUsed3); | ||
| uint256 minGas = gasUsed1 < gasUsed2 | ||
| ? (gasUsed1 < gasUsed3 ? gasUsed1 : gasUsed3) | ||
| : (gasUsed2 < gasUsed3 ? gasUsed2 : gasUsed3); | ||
| assertTrue( | ||
| maxGas - minGas < 30000, | ||
| "Gas usage difference should be reasonable for same-length keys" | ||
| ); | ||
| } | ||
|
Check warning on line 159 in contracts/ext-interop/tests/ValidatorSetGasTests.t.sol
|
||
|
|
||
| function testGasUsageReadOperations() public { | ||
| emit log_string("=== Testing gas usage for read operations ==="); | ||
|
|
||
| // Register some validators first | ||
| uint256 validatorId1 = _validatorSet.registerValidator(1000, PUBLIC_KEY); | ||
| _validatorSet.registerValidator(2000, PUBLIC_KEY); | ||
| _validatorSet.registerValidator(3000, PUBLIC_KEY); | ||
|
|
||
| // Test getValidator | ||
| uint256 gasStart1 = gasleft(); | ||
| _validatorSet.getValidator(validatorId1); | ||
| uint256 gasUsed1 = gasStart1 - gasleft(); | ||
|
|
||
| // Test getTotalValidators | ||
| uint256 gasStart2 = gasleft(); | ||
| _validatorSet.getTotalValidators(); | ||
| uint256 gasUsed2 = gasStart2 - gasleft(); | ||
|
|
||
| emit log_named_uint("Gas used for getValidator", gasUsed1); | ||
| emit log_named_uint("Gas used for getTotalValidators", gasUsed2); | ||
|
|
||
| // Read operations should be relatively cheap | ||
| assertTrue(gasUsed1 < 10000, "getValidator should be relatively cheap"); | ||
| assertTrue(gasUsed2 < 5000, "getTotalValidators should be very cheap"); | ||
| } | ||
|
Check warning on line 185 in contracts/ext-interop/tests/ValidatorSetGasTests.t.sol
|
||
|
|
||
| function testGasUsageBatchOperations() public { | ||
| emit log_string("=== Testing gas usage for batch operations ==="); | ||
|
|
||
| address[] memory validators = new address[](5); | ||
| validators[0] = address(0x100); | ||
| validators[1] = address(0x200); | ||
| validators[2] = address(0x300); | ||
| validators[3] = address(0x400); | ||
| validators[4] = address(0x500); | ||
|
|
||
| uint256 totalGasUsed = 0; | ||
| uint256 gasStart = gasleft(); | ||
|
|
||
| // Register 5 validators in sequence | ||
| for (uint256 i = 0; i < validators.length; i++) { | ||
| uint256 iterationGasStart = gasleft(); | ||
| _validatorSet.registerValidator(1000 + i, PUBLIC_KEY); | ||
| uint256 iterationGasUsed = iterationGasStart - gasleft(); | ||
| emit log_named_uint( | ||
| string(abi.encodePacked("Gas for registration ", vm.toString(i + 1))), | ||
| iterationGasUsed | ||
| ); | ||
| } | ||
|
|
||
| totalGasUsed = gasStart - gasleft(); | ||
| emit log_named_uint("Total gas used for 5 registrations", totalGasUsed); | ||
| emit log_named_uint("Average gas per registration", totalGasUsed / validators.length); | ||
| } | ||
|
Check warning on line 214 in contracts/ext-interop/tests/ValidatorSetGasTests.t.sol
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // SPDX-License-Identifier: BSD-3-Clause-Clear | ||
| pragma solidity ^0.8.25; | ||
|
|
||
| /** | ||
| * @title ValidatorSet | ||
| * @dev Contract for managing validator information using struct storage in arrays | ||
| */ | ||
| contract ValidatorSet { | ||
| // Struct to represent validator information | ||
| struct Validator { | ||
| uint256 weight; | ||
| bytes publicKey; | ||
| } | ||
|
|
||
| // Array to store validators | ||
| Validator[] public validators; | ||
|
|
||
| /** | ||
| * @dev Public function to save/register a new validator | ||
| * @param weight The weight of the validator | ||
| * @param publicKey The public key of the validator | ||
| */ | ||
| function registerValidator( | ||
| uint256 weight, | ||
| bytes memory publicKey | ||
| ) public returns (uint256 validatorId) { | ||
| // Create and push the validator struct to the array | ||
| validators.push(Validator({weight: weight, publicKey: publicKey})); | ||
|
|
||
| // Return the index (validator ID) - arrays are 0-indexed | ||
| validatorId = validators.length - 1; | ||
| return validatorId; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Public function to update an existing validator's information | ||
| * @param validatorId The ID of the validator to update | ||
| * @param weight The new weight of the validator | ||
| * @param publicKey The new public key of the validator | ||
| */ | ||
| function updateValidator(uint256 validatorId, uint256 weight, bytes memory publicKey) public { | ||
| require(validatorId < validators.length, "Invalid validator ID"); | ||
|
Check notice on line 42 in contracts/ext-interop/validator-set.sol
|
||
|
|
||
| // Update the validator struct in the array | ||
| validators[validatorId].weight = weight; | ||
| validators[validatorId].publicKey = publicKey; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Get validator information by ID | ||
| * @param validatorId The ID of the validator | ||
| * @return validator The validator struct | ||
| */ | ||
| function getValidator( | ||
| uint256 validatorId | ||
| ) public view returns (Validator memory) { | ||
| require(validatorId < validators.length, "Invalid validator ID"); | ||
|
Check notice on line 57 in contracts/ext-interop/validator-set.sol
|
||
Check noticeCode scanning / Semgrep PRO Semgrep Finding: solidity.performance.use-custom-error-not-require.use-custom-error-not-require Note
Consider using custom errors as they are more gas efficient while allowing developers to describe the error in detail using NatSpec.
|
||
| return validators[validatorId]; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Get the total number of registered validators | ||
| * @return count The total count of validators | ||
| */ | ||
| function getTotalValidators() public view returns (uint256) { | ||
| return validators.length; | ||
| } | ||
| } | ||
Check notice
Code scanning / Semgrep PRO
Semgrep Finding: solidity.performance.use-custom-error-not-require.use-custom-error-not-require Note