From 518cc281b14727c26cd7fcb9db882b45837d443a Mon Sep 17 00:00:00 2001 From: kanth <70688600+defistar@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:26:04 +0530 Subject: [PATCH 1/2] feat: CCIP-2465 enumerableMap for addressToBytes Mapping (#14012) * feat: CCIP-2465 enumerableMap for addressToBytes Mapping * fix: CCIP-2465 prettier write for new library contracts * fix: CCIP-2465 gas-snapshot updated and changeset added for new library * fix: CCIP-2465 gassnapshot corrections for EnumerableAddresses --- contracts/.changeset/nice-planets-share.md | 5 + contracts/gas-snapshots/shared.gas-snapshot | 28 ++-- .../enumerable/EnumerableMapAddresses.sol | 89 +++++++++++- .../enumerable/EnumerableMapBytes32.sol | 136 ++++++++++++++++++ .../enumerable/EnumerableMapAddresses.t.sol | 111 +++++++++++--- 5 files changed, 338 insertions(+), 31 deletions(-) create mode 100644 contracts/.changeset/nice-planets-share.md create mode 100644 contracts/src/v0.8/shared/enumerable/EnumerableMapBytes32.sol diff --git a/contracts/.changeset/nice-planets-share.md b/contracts/.changeset/nice-planets-share.md new file mode 100644 index 00000000000..a8af56ac613 --- /dev/null +++ b/contracts/.changeset/nice-planets-share.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': minor +--- + +EnumerableMap Library for an Address to Bytes mapping diff --git a/contracts/gas-snapshots/shared.gas-snapshot b/contracts/gas-snapshots/shared.gas-snapshot index 0419c42a6aa..3cc143ecc0e 100644 --- a/contracts/gas-snapshots/shared.gas-snapshot +++ b/contracts/gas-snapshots/shared.gas-snapshot @@ -51,21 +51,29 @@ CallWithExactGas__callWithExactGasSafeReturnData:test_NoContractReverts() (gas: CallWithExactGas__callWithExactGasSafeReturnData:test_NoGasForCallExactCheckReverts() (gas: 16139) CallWithExactGas__callWithExactGasSafeReturnData:test_NotEnoughGasForCallReverts() (gas: 16547) CallWithExactGas__callWithExactGasSafeReturnData:test_callWithExactGasSafeReturnData_ThrowOOGError_Revert() (gas: 36752) -EnumerableMapAddresses_at:testAtSuccess() (gas: 95001) -EnumerableMapAddresses_at:testBytes32AtSuccess() (gas: 94770) +EnumerableMapAddresses_at:testAtSuccess() (gas: 95086) +EnumerableMapAddresses_at:testBytes32AtSuccess() (gas: 94877) EnumerableMapAddresses_contains:testBytes32ContainsSuccess() (gas: 93518) EnumerableMapAddresses_contains:testContainsSuccess() (gas: 93696) -EnumerableMapAddresses_get:testBytes32GetSuccess() (gas: 94249) -EnumerableMapAddresses_get:testGetSuccess() (gas: 94436) -EnumerableMapAddresses_get_errorMessage:testGetErrorMessageSuccess() (gas: 94477) -EnumerableMapAddresses_length:testBytes32LengthSuccess() (gas: 72404) -EnumerableMapAddresses_length:testLengthSuccess() (gas: 72582) -EnumerableMapAddresses_remove:testBytes32RemoveSuccess() (gas: 73408) +EnumerableMapAddresses_get:testBytes32GetSuccess() (gas: 94278) +EnumerableMapAddresses_get:testGetSuccess() (gas: 94453) +EnumerableMapAddresses_get_errorMessage:testGetErrorMessageSuccess() (gas: 94489) +EnumerableMapAddresses_length:testBytes32LengthSuccess() (gas: 72445) +EnumerableMapAddresses_length:testLengthSuccess() (gas: 72640) +EnumerableMapAddresses_remove:testBytes32RemoveSuccess() (gas: 73462) EnumerableMapAddresses_remove:testRemoveSuccess() (gas: 73686) EnumerableMapAddresses_set:testBytes32SetSuccess() (gas: 94496) EnumerableMapAddresses_set:testSetSuccess() (gas: 94685) -EnumerableMapAddresses_tryGet:testBytes32TryGetSuccess() (gas: 94604) -EnumerableMapAddresses_tryGet:testTryGetSuccess() (gas: 94864) +EnumerableMapAddresses_tryGet:testBytes32TryGetSuccess() (gas: 94622) +EnumerableMapAddresses_tryGet:testTryGetSuccess() (gas: 94893) +EnumerableMapAddresses_at:testBytesAtSuccess() (gas: 96564) +EnumerableMapAddresses_contains:testBytesContainsSuccess() (gas: 94012) +EnumerableMapAddresses_get:testBytesGetSuccess() (gas: 95879) +EnumerableMapAddresses_get_errorMessage:testBytesGetErrorMessageSuccess() (gas: 95878) +EnumerableMapAddresses_length:testBytesLengthSuccess() (gas: 73011) +EnumerableMapAddresses_remove:testBytesRemoveSuccess() (gas: 74249) +EnumerableMapAddresses_set:testBytesSetSuccess() (gas: 95428) +EnumerableMapAddresses_tryGet:testBytesTryGetSuccess() (gas: 96279) OpStackBurnMintERC677_constructor:testConstructorSuccess() (gas: 1743649) OpStackBurnMintERC677_interfaceCompatibility:testBurnCompatibility() (gas: 298649) OpStackBurnMintERC677_interfaceCompatibility:testMintCompatibility() (gas: 137957) diff --git a/contracts/src/v0.8/shared/enumerable/EnumerableMapAddresses.sol b/contracts/src/v0.8/shared/enumerable/EnumerableMapAddresses.sol index 6fbd37c60d7..c14a03b4443 100644 --- a/contracts/src/v0.8/shared/enumerable/EnumerableMapAddresses.sol +++ b/contracts/src/v0.8/shared/enumerable/EnumerableMapAddresses.sol @@ -1,12 +1,15 @@ // SPDX-License-Identifier: MIT +/* solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore */ pragma solidity ^0.8.0; import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol"; +import {EnumerableMapBytes32} from "./EnumerableMapBytes32.sol"; // TODO: the lib can be replaced with OZ v5.1 post-upgrade, which has AddressToAddressMap and AddressToBytes32Map library EnumerableMapAddresses { using EnumerableMap for EnumerableMap.UintToAddressMap; using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map; + using EnumerableMapBytes32 for EnumerableMapBytes32.Bytes32ToBytesMap; struct AddressToAddressMap { EnumerableMap.UintToAddressMap _inner; @@ -57,8 +60,6 @@ library EnumerableMapAddresses { return map._inner.get(uint256(uint160(key)), errorMessage); } - // AddressToBytes32Map - struct AddressToBytes32Map { EnumerableMap.Bytes32ToBytes32Map _inner; } @@ -137,4 +138,88 @@ library EnumerableMapAddresses { function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) { return map._inner.get(bytes32(uint256(uint160(key)))); } + + struct AddressToBytesMap { + EnumerableMapBytes32.Bytes32ToBytesMap _inner; + } + + /** + * @dev Sets the value for `key` in the map. Returns true if the key was added to the map, that is if it was not already present. + * @param map The map where the value will be set + * @param key The key to set the value for + * @param value The value to set for the key + * @return bool indicating whether the key was added to the map + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function set(AddressToBytesMap storage map, address key, bytes memory value) internal returns (bool) { + return map._inner.set(bytes32(uint256(uint160(key))), value); + } + + /** + * @dev Removes the value for `key` in the map. Returns true if the key was removed from the map, that is if it was present. + * @param map The map where the value will be removed + * @param key The key to remove the value for + * @return bool indicating whether the key was removed from the map + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function remove(AddressToBytesMap storage map, address key) internal returns (bool) { + return map._inner.remove(bytes32(uint256(uint160(key)))); + } + + /** + * @dev Checks if the map contains the `key`. Returns true if the key is in the map. + * @param map The map to check for the presence of the key + * @param key The key to check for presence in the map + * @return bool indicating whether the key is in the map + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function contains(AddressToBytesMap storage map, address key) internal view returns (bool) { + return map._inner.contains(bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns the number of elements in the map. + * @param map The map to check the length of + * @return uint256 indicating the number of elements in the map + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function length(AddressToBytesMap storage map) internal view returns (uint256) { + return map._inner.length(); + } + + /** + * @dev Returns the element stored at position `index` in the map. Note that there are no guarantees on the ordering of values inside the array, and it may change when more values are added or removed. + * @param map The map to retrieve the element from + * @param index The index to retrieve the element at + * @return address The key of the element at the specified index + * @return bytes The value of the element at the specified index + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function at(AddressToBytesMap storage map, uint256 index) internal view returns (address, bytes memory) { + (bytes32 key, bytes memory value) = map._inner.at(index); + return (address(uint160(uint256(key))), value); + } + + /** + * @dev Tries to return the value associated with `key`. Does not revert if `key` is not in the map. + * @param map The map to retrieve the value from + * @param key The key to retrieve the value for + * @return bool indicating whether the key was in the map + * @return bytes The value associated with the key + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function tryGet(AddressToBytesMap storage map, address key) internal view returns (bool, bytes memory) { + return map._inner.tryGet(bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns the value associated with `key`. + * @param map The map to retrieve the value from + * @param key The key to retrieve the value for + * @return bytes The value associated with the key + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function get(AddressToBytesMap storage map, address key) internal view returns (bytes memory) { + return map._inner.get(bytes32(uint256(uint160(key)))); + } } diff --git a/contracts/src/v0.8/shared/enumerable/EnumerableMapBytes32.sol b/contracts/src/v0.8/shared/enumerable/EnumerableMapBytes32.sol new file mode 100644 index 00000000000..2ec9098f855 --- /dev/null +++ b/contracts/src/v0.8/shared/enumerable/EnumerableMapBytes32.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: MIT +/* solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore */ +pragma solidity ^0.8.0; + +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; + +/** + * @dev Library for managing an enumerable variant of Solidity's + * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] + * type. + * + * Maps have the following properties: + * + * - Entries are added, removed, and checked for existence in constant time + * (O(1)). + * - Entries are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableMapBytes32 for EnumerableMapBytes32.Bytes32ToBytesMap; + * + * // Declare a set state variable + * EnumerableMapBytes32.Bytes32ToBytesMap private myMap; + * } + * ``` + * + * The following map types are supported: + * + * - `bytes32 -> bytes` (`Bytes32ToBytes`) + * + * [WARNING] + * ==== + * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure + * unusable. + * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. + * + * In order to clean up an EnumerableMapBytes32, you should remove all elements one by one. + * ==== + */ +library EnumerableMapBytes32 { + using EnumerableSet for EnumerableSet.Bytes32Set; + + error NonexistentKeyError(); + + struct Bytes32ToBytesMap { + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 => bytes) _values; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function set(Bytes32ToBytesMap storage map, bytes32 key, bytes memory value) internal returns (bool) { + map._values[key] = value; + return map._keys.add(key); + } + + /** + * @dev Removes a key-value pair from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function remove(Bytes32ToBytesMap storage map, bytes32 key) internal returns (bool) { + delete map._values[key]; + return map._keys.remove(key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function contains(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bool) { + return map._keys.contains(key); + } + + /** + * @dev Returns the number of key-value pairs in the map. O(1). + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function length(Bytes32ToBytesMap storage map) internal view returns (uint256) { + return map._keys.length(); + } + + /** + * @dev Returns the key-value pair stored at position `index` in the map. O(1). + * + * Note that there are no guarantees on the ordering of entries inside the + * array, and it may change when more entries are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function at(Bytes32ToBytesMap storage map, uint256 index) internal view returns (bytes32, bytes memory) { + bytes32 key = map._keys.at(index); + return (key, map._values[key]); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function tryGet(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bool, bytes memory) { + bytes memory value = map._values[key]; + if (value.length == 0) { + return (contains(map, key), bytes("")); + } else { + return (true, value); + } + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore + function get(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bytes memory) { + bytes memory value = map._values[key]; + if (value.length == 0 && !contains(map, key)) { + revert NonexistentKeyError(); + } + return value; + } +} diff --git a/contracts/src/v0.8/shared/test/enumerable/EnumerableMapAddresses.t.sol b/contracts/src/v0.8/shared/test/enumerable/EnumerableMapAddresses.t.sol index 900c546f66d..097e79e372e 100644 --- a/contracts/src/v0.8/shared/test/enumerable/EnumerableMapAddresses.t.sol +++ b/contracts/src/v0.8/shared/test/enumerable/EnumerableMapAddresses.t.sol @@ -7,11 +7,14 @@ import {EnumerableMapAddresses} from "../../enumerable/EnumerableMapAddresses.so contract EnumerableMapAddressesTest is BaseTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; EnumerableMapAddresses.AddressToAddressMap internal s_addressToAddressMap; EnumerableMapAddresses.AddressToBytes32Map internal s_addressToBytes32Map; + EnumerableMapAddresses.AddressToBytesMap internal s_addressToBytesMap; bytes32 internal constant MOCK_BYTES32_VALUE = bytes32(uint256(42)); + bytes internal constant MOCK_BYTES_VALUE = "0x123456789abcdef"; function setUp() public virtual override { BaseTest.setUp(); @@ -21,6 +24,7 @@ contract EnumerableMapAddressesTest is BaseTest { contract EnumerableMapAddresses_set is EnumerableMapAddressesTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; function testSetSuccess() public { assertTrue(!s_addressToAddressMap.contains(address(this))); @@ -35,11 +39,19 @@ contract EnumerableMapAddresses_set is EnumerableMapAddressesTest { assertTrue(s_addressToBytes32Map.contains(address(this))); assertTrue(!s_addressToBytes32Map.set(address(this), MOCK_BYTES32_VALUE)); } + + function testBytesSetSuccess() public { + assertTrue(!s_addressToBytesMap.contains(address(this))); + assertTrue(s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + assertTrue(s_addressToBytesMap.contains(address(this))); + assertTrue(!s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + } } contract EnumerableMapAddresses_remove is EnumerableMapAddressesTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; function testRemoveSuccess() public { assertTrue(!s_addressToAddressMap.contains(address(this))); @@ -58,11 +70,21 @@ contract EnumerableMapAddresses_remove is EnumerableMapAddressesTest { assertTrue(!s_addressToBytes32Map.contains(address(this))); assertTrue(!s_addressToBytes32Map.remove(address(this))); } + + function testBytesRemoveSuccess() public { + assertTrue(!s_addressToBytesMap.contains(address(this))); + assertTrue(s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + assertTrue(s_addressToBytesMap.contains(address(this))); + assertTrue(s_addressToBytesMap.remove(address(this))); + assertTrue(!s_addressToBytesMap.contains(address(this))); + assertTrue(!s_addressToBytesMap.remove(address(this))); + } } contract EnumerableMapAddresses_contains is EnumerableMapAddressesTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; function testContainsSuccess() public { assertTrue(!s_addressToAddressMap.contains(address(this))); @@ -75,55 +97,81 @@ contract EnumerableMapAddresses_contains is EnumerableMapAddressesTest { assertTrue(s_addressToBytes32Map.set(address(this), MOCK_BYTES32_VALUE)); assertTrue(s_addressToBytes32Map.contains(address(this))); } + + function testBytesContainsSuccess() public { + assertTrue(!s_addressToBytesMap.contains(address(this))); + assertTrue(s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + assertTrue(s_addressToBytesMap.contains(address(this))); + } } contract EnumerableMapAddresses_length is EnumerableMapAddressesTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; function testLengthSuccess() public { - assertTrue(s_addressToAddressMap.length() == 0); + assertEq(s_addressToAddressMap.length(), 0); assertTrue(s_addressToAddressMap.set(address(this), address(this))); - assertTrue(s_addressToAddressMap.length() == 1); + assertEq(s_addressToAddressMap.length(), 1); assertTrue(s_addressToAddressMap.remove(address(this))); - assertTrue(s_addressToAddressMap.length() == 0); + assertEq(s_addressToAddressMap.length(), 0); } function testBytes32LengthSuccess() public { - assertTrue(s_addressToBytes32Map.length() == 0); + assertEq(s_addressToBytes32Map.length(), 0); assertTrue(s_addressToBytes32Map.set(address(this), MOCK_BYTES32_VALUE)); - assertTrue(s_addressToBytes32Map.length() == 1); + assertEq(s_addressToBytes32Map.length(), 1); assertTrue(s_addressToBytes32Map.remove(address(this))); - assertTrue(s_addressToBytes32Map.length() == 0); + assertEq(s_addressToBytes32Map.length(), 0); + } + + function testBytesLengthSuccess() public { + assertEq(s_addressToBytesMap.length(), 0); + assertTrue(s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + assertEq(s_addressToBytesMap.length(), 1); + assertTrue(s_addressToBytesMap.remove(address(this))); + assertEq(s_addressToBytesMap.length(), 0); } } contract EnumerableMapAddresses_at is EnumerableMapAddressesTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; function testAtSuccess() public { - assertTrue(s_addressToAddressMap.length() == 0); + assertEq(s_addressToAddressMap.length(), 0); assertTrue(s_addressToAddressMap.set(address(this), address(this))); - assertTrue(s_addressToAddressMap.length() == 1); + assertEq(s_addressToAddressMap.length(), 1); (address key, address value) = s_addressToAddressMap.at(0); - assertTrue(key == address(this)); - assertTrue(value == address(this)); + assertEq(key, address(this)); + assertEq(value, address(this)); } function testBytes32AtSuccess() public { - assertTrue(s_addressToBytes32Map.length() == 0); + assertEq(s_addressToBytes32Map.length(), 0); assertTrue(s_addressToBytes32Map.set(address(this), MOCK_BYTES32_VALUE)); - assertTrue(s_addressToBytes32Map.length() == 1); + assertEq(s_addressToBytes32Map.length(), 1); (address key, bytes32 value) = s_addressToBytes32Map.at(0); - assertTrue(key == address(this)); - assertTrue(value == MOCK_BYTES32_VALUE); + assertEq(key, address(this)); + assertEq(value, MOCK_BYTES32_VALUE); + } + + function testBytesAtSuccess() public { + assertEq(s_addressToBytesMap.length(), 0); + assertTrue(s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + assertEq(s_addressToBytesMap.length(), 1); + (address key, bytes memory value) = s_addressToBytesMap.at(0); + assertEq(key, address(this)); + assertEq(value, MOCK_BYTES_VALUE); } } contract EnumerableMapAddresses_tryGet is EnumerableMapAddressesTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; function testTryGetSuccess() public { assertTrue(!s_addressToAddressMap.contains(address(this))); @@ -131,7 +179,7 @@ contract EnumerableMapAddresses_tryGet is EnumerableMapAddressesTest { assertTrue(s_addressToAddressMap.contains(address(this))); (bool success, address value) = s_addressToAddressMap.tryGet(address(this)); assertTrue(success); - assertTrue(value == address(this)); + assertEq(value, address(this)); } function testBytes32TryGetSuccess() public { @@ -140,37 +188,62 @@ contract EnumerableMapAddresses_tryGet is EnumerableMapAddressesTest { assertTrue(s_addressToBytes32Map.contains(address(this))); (bool success, bytes32 value) = s_addressToBytes32Map.tryGet(address(this)); assertTrue(success); - assertTrue(value == MOCK_BYTES32_VALUE); + assertEq(value, MOCK_BYTES32_VALUE); + } + + function testBytesTryGetSuccess() public { + assertTrue(!s_addressToBytesMap.contains(address(this))); + assertTrue(s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + assertTrue(s_addressToBytesMap.contains(address(this))); + (bool success, bytes memory value) = s_addressToBytesMap.tryGet(address(this)); + assertTrue(success); + assertEq(value, MOCK_BYTES_VALUE); } } contract EnumerableMapAddresses_get is EnumerableMapAddressesTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; function testGetSuccess() public { assertTrue(!s_addressToAddressMap.contains(address(this))); assertTrue(s_addressToAddressMap.set(address(this), address(this))); assertTrue(s_addressToAddressMap.contains(address(this))); - assertTrue(s_addressToAddressMap.get(address(this)) == address(this)); + assertEq(s_addressToAddressMap.get(address(this)), address(this)); } function testBytes32GetSuccess() public { assertTrue(!s_addressToBytes32Map.contains(address(this))); assertTrue(s_addressToBytes32Map.set(address(this), MOCK_BYTES32_VALUE)); assertTrue(s_addressToBytes32Map.contains(address(this))); - assertTrue(s_addressToBytes32Map.get(address(this)) == MOCK_BYTES32_VALUE); + assertEq(s_addressToBytes32Map.get(address(this)), MOCK_BYTES32_VALUE); + } + + function testBytesGetSuccess() public { + assertTrue(!s_addressToBytesMap.contains(address(this))); + assertTrue(s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + assertTrue(s_addressToBytesMap.contains(address(this))); + assertEq(s_addressToBytesMap.get(address(this)), MOCK_BYTES_VALUE); } } contract EnumerableMapAddresses_get_errorMessage is EnumerableMapAddressesTest { using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map; using EnumerableMapAddresses for EnumerableMapAddresses.AddressToAddressMap; + using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytesMap; function testGetErrorMessageSuccess() public { assertTrue(!s_addressToAddressMap.contains(address(this))); assertTrue(s_addressToAddressMap.set(address(this), address(this))); assertTrue(s_addressToAddressMap.contains(address(this))); - assertTrue(s_addressToAddressMap.get(address(this), "EnumerableMapAddresses: nonexistent key") == address(this)); + assertEq(s_addressToAddressMap.get(address(this), "EnumerableMapAddresses: nonexistent key"), address(this)); + } + + function testBytesGetErrorMessageSuccess() public { + assertTrue(!s_addressToBytesMap.contains(address(this))); + assertTrue(s_addressToBytesMap.set(address(this), MOCK_BYTES_VALUE)); + assertTrue(s_addressToBytesMap.contains(address(this))); + assertEq(s_addressToBytesMap.get(address(this)), MOCK_BYTES_VALUE); } } From 2b86088670596de517b35d652c85a031f0fb6faf Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:54:53 +0400 Subject: [PATCH 2/2] refactor + support v2.3 benchmark test (#14052) * update feeds used in automation tests * comment unused vars * support v2.3 in benchmark test --- .../actions/automation_ocr_helpers.go | 2 +- .../actions/automationv2/actions.go | 67 +++++++++---- integration-tests/actions/keeper_helpers.go | 6 +- integration-tests/benchmark/keeper_test.go | 95 +++++++------------ .../contracts/contract_models.go | 2 +- .../contracts/ethereum_contracts.go | 20 ++-- .../contracts/ethereum_keeper_contracts.go | 1 + .../testconfig/keeper/keeper.toml | 67 ++++++++++++- .../testsetups/keeper_benchmark.go | 28 +++--- 9 files changed, 178 insertions(+), 110 deletions(-) diff --git a/integration-tests/actions/automation_ocr_helpers.go b/integration-tests/actions/automation_ocr_helpers.go index 05c4501fbe6..3e552371f98 100644 --- a/integration-tests/actions/automation_ocr_helpers.go +++ b/integration-tests/actions/automation_ocr_helpers.go @@ -438,7 +438,7 @@ func deployRegistry( wethToken contracts.WETHToken, ethUSDFeed contracts.MockETHUSDFeed, ) contracts.KeeperRegistry { - ef, err := contracts.DeployMockETHLINKFeed(client, big.NewInt(2e18)) + ef, err := contracts.DeployMockLINKETHFeed(client, big.NewInt(2e18)) require.NoError(t, err, "Deploying mock ETH-Link feed shouldn't fail") gf, err := contracts.DeployMockGASFeed(client, big.NewInt(2e11)) require.NoError(t, err, "Deploying mock gas feed shouldn't fail") diff --git a/integration-tests/actions/automationv2/actions.go b/integration-tests/actions/automationv2/actions.go index 40caf15917b..9075b863b65 100644 --- a/integration-tests/actions/automationv2/actions.go +++ b/integration-tests/actions/automationv2/actions.go @@ -62,8 +62,9 @@ type AutomationTest struct { LinkToken contracts.LinkToken Transcoder contracts.UpkeepTranscoder - EthLinkFeed contracts.MockETHLINKFeed - EthUSDFeed contracts.MockETHUSDFeed + LINKETHFeed contracts.MockLINKETHFeed + ETHUSDFeed contracts.MockETHUSDFeed + LINKUSDFeed contracts.MockETHUSDFeed WETHToken contracts.WETHToken GasFeed contracts.MockGasFeed Registry contracts.KeeperRegistry @@ -192,31 +193,30 @@ func (a *AutomationTest) LoadTranscoder(address string) error { return nil } -func (a *AutomationTest) DeployEthLinkFeed() error { - ethLinkFeed, err := contracts.DeployMockETHLINKFeed(a.ChainClient, a.RegistrySettings.FallbackLinkPrice) +func (a *AutomationTest) DeployLinkEthFeed() error { + ethLinkFeed, err := contracts.DeployMockLINKETHFeed(a.ChainClient, a.RegistrySettings.FallbackLinkPrice) if err != nil { return err } - a.EthLinkFeed = ethLinkFeed + a.LINKETHFeed = ethLinkFeed return nil } -func (a *AutomationTest) LoadEthLinkFeed(address string) error { - ethLinkFeed, err := contracts.LoadMockETHLINKFeed(a.ChainClient, common.HexToAddress(address)) +func (a *AutomationTest) LoadLinkEthFeed(address string) error { + ethLinkFeed, err := contracts.LoadMockLINKETHFeed(a.ChainClient, common.HexToAddress(address)) if err != nil { return err } - a.EthLinkFeed = ethLinkFeed + a.LINKETHFeed = ethLinkFeed return nil } func (a *AutomationTest) DeployEthUSDFeed() error { - // FallbackLinkPrice and FallbackETHPrice are the same ethUSDFeed, err := contracts.DeployMockETHUSDFeed(a.ChainClient, a.RegistrySettings.FallbackLinkPrice) if err != nil { return err } - a.EthUSDFeed = ethUSDFeed + a.ETHUSDFeed = ethUSDFeed return nil } @@ -225,7 +225,25 @@ func (a *AutomationTest) LoadEthUSDFeed(address string) error { if err != nil { return err } - a.EthUSDFeed = ethUSDFeed + a.ETHUSDFeed = ethUSDFeed + return nil +} + +func (a *AutomationTest) DeployLinkUSDFeed() error { + linkUSDFeed, err := contracts.DeployMockETHUSDFeed(a.ChainClient, a.RegistrySettings.FallbackLinkPrice) + if err != nil { + return err + } + a.LINKUSDFeed = linkUSDFeed + return nil +} + +func (a *AutomationTest) LoadLinkUSDFeed(address string) error { + linkUSDFeed, err := contracts.LoadMockETHUSDFeed(a.ChainClient, common.HexToAddress(address)) + if err != nil { + return err + } + a.LINKUSDFeed = linkUSDFeed return nil } @@ -269,13 +287,13 @@ func (a *AutomationTest) DeployRegistry() error { registryOpts := &contracts.KeeperRegistryOpts{ RegistryVersion: a.RegistrySettings.RegistryVersion, LinkAddr: a.LinkToken.Address(), - ETHFeedAddr: a.EthLinkFeed.Address(), + ETHFeedAddr: a.LINKETHFeed.Address(), GasFeedAddr: a.GasFeed.Address(), TranscoderAddr: a.Transcoder.Address(), RegistrarAddr: utils.ZeroAddress.Hex(), Settings: a.RegistrySettings, - LinkUSDFeedAddr: a.EthUSDFeed.Address(), - NativeUSDFeedAddr: a.EthUSDFeed.Address(), + LinkUSDFeedAddr: a.ETHUSDFeed.Address(), + NativeUSDFeedAddr: a.LINKUSDFeed.Address(), WrappedNativeAddr: a.WETHToken.Address(), } registry, err := contracts.DeployKeeperRegistry(a.ChainClient, registryOpts) @@ -563,7 +581,7 @@ func (a *AutomationTest) SetConfigOnRegistry() error { { GasFeePPB: 100, FlatFeeMilliCents: big.NewInt(500), - PriceFeed: common.HexToAddress(a.EthUSDFeed.Address()), // ETH/USD feed and LINK/USD feed are the same + PriceFeed: common.HexToAddress(a.ETHUSDFeed.Address()), Decimals: 18, FallbackPrice: big.NewInt(1000), MinSpend: big.NewInt(200), @@ -571,7 +589,7 @@ func (a *AutomationTest) SetConfigOnRegistry() error { { GasFeePPB: 100, FlatFeeMilliCents: big.NewInt(500), - PriceFeed: common.HexToAddress(a.EthUSDFeed.Address()), // ETH/USD feed and LINK/USD feed are the same + PriceFeed: common.HexToAddress(a.LINKUSDFeed.Address()), Decimals: 18, FallbackPrice: big.NewInt(1000), MinSpend: big.NewInt(200), @@ -853,14 +871,17 @@ func (a *AutomationTest) SetupAutomationDeployment(t *testing.T) { err = a.DeployWETH() require.NoError(t, err, "Error deploying weth token contract") - err = a.DeployEthLinkFeed() - require.NoError(t, err, "Error deploying eth link feed contract") + err = a.DeployLinkEthFeed() + require.NoError(t, err, "Error deploying link eth feed contract") err = a.DeployGasFeed() require.NoError(t, err, "Error deploying gas feed contract") err = a.DeployEthUSDFeed() require.NoError(t, err, "Error deploying eth usd feed contract") + err = a.DeployLinkUSDFeed() + require.NoError(t, err, "Error deploying link usd feed contract") + err = a.DeployTranscoder() require.NoError(t, err, "Error deploying transcoder contract") @@ -873,7 +894,7 @@ func (a *AutomationTest) SetupAutomationDeployment(t *testing.T) { } func (a *AutomationTest) LoadAutomationDeployment(t *testing.T, linkTokenAddress, - ethLinkFeedAddress, gasFeedAddress, transcoderAddress, registryAddress, registrarAddress string) { + linkEthFeedAddress, linkUsdFeedAddress, EthUsdFeedAddress, gasFeedAddress, transcoderAddress, registryAddress, registrarAddress string) { l := logging.GetTestLogger(t) err := a.CollectNodeDetails() require.NoError(t, err, "Error collecting node details") @@ -883,10 +904,14 @@ func (a *AutomationTest) LoadAutomationDeployment(t *testing.T, linkTokenAddress err = a.LoadLINK(linkTokenAddress) require.NoError(t, err, "Error loading link token contract") - err = a.LoadEthLinkFeed(ethLinkFeedAddress) - require.NoError(t, err, "Error loading eth link feed contract") + err = a.LoadLinkEthFeed(linkEthFeedAddress) + require.NoError(t, err, "Error loading link eth feed contract") err = a.LoadEthGasFeed(gasFeedAddress) require.NoError(t, err, "Error loading gas feed contract") + err = a.LoadEthUSDFeed(EthUsdFeedAddress) + require.NoError(t, err, "Error loading eth usd feed contract") + err = a.LoadLinkUSDFeed(linkUsdFeedAddress) + require.NoError(t, err, "Error loading link usd feed contract") err = a.LoadTranscoder(transcoderAddress) require.NoError(t, err, "Error loading transcoder contract") err = a.LoadRegistry(registryAddress) diff --git a/integration-tests/actions/keeper_helpers.go b/integration-tests/actions/keeper_helpers.go index ee1662cc180..618ca969334 100644 --- a/integration-tests/actions/keeper_helpers.go +++ b/integration-tests/actions/keeper_helpers.go @@ -81,7 +81,7 @@ func DeployKeeperContracts( client *seth.Client, linkFundsForEachUpkeep *big.Int, ) (contracts.KeeperRegistry, contracts.KeeperRegistrar, []contracts.KeeperConsumer, []*big.Int) { - ef, err := contracts.DeployMockETHLINKFeed(client, big.NewInt(2e18)) + ef, err := contracts.DeployMockLINKETHFeed(client, big.NewInt(2e18)) require.NoError(t, err, "Deploying mock ETH-Link feed shouldn't fail") gf, err := contracts.DeployMockGASFeed(client, big.NewInt(2e11)) require.NoError(t, err, "Deploying mock gas feed shouldn't fail") @@ -136,7 +136,7 @@ func DeployPerformanceKeeperContracts( checkGasToBurn, // How much gas should be burned on checkUpkeep() calls performGasToBurn int64, // How much gas should be burned on performUpkeep() calls ) (contracts.KeeperRegistry, contracts.KeeperRegistrar, []contracts.KeeperConsumerPerformance, []*big.Int) { - ef, err := contracts.DeployMockETHLINKFeed(chainClient, big.NewInt(2e18)) + ef, err := contracts.DeployMockLINKETHFeed(chainClient, big.NewInt(2e18)) require.NoError(t, err, "Deploying mock ETH-Link feed shouldn't fail") gf, err := contracts.DeployMockGASFeed(chainClient, big.NewInt(2e11)) require.NoError(t, err, "Deploying mock gas feed shouldn't fail") @@ -196,7 +196,7 @@ func DeployPerformDataCheckerContracts( linkFundsForEachUpkeep *big.Int, expectedData []byte, ) (contracts.KeeperRegistry, contracts.KeeperRegistrar, []contracts.KeeperPerformDataChecker, []*big.Int) { - ef, err := contracts.DeployMockETHLINKFeed(chainClient, big.NewInt(2e18)) + ef, err := contracts.DeployMockLINKETHFeed(chainClient, big.NewInt(2e18)) require.NoError(t, err, "Deploying mock ETH-Link feed shouldn't fail") gf, err := contracts.DeployMockGASFeed(chainClient, big.NewInt(2e11)) require.NoError(t, err, "Deploying mock gas feed shouldn't fail") diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 177b3521013..fde550bbdf4 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -10,26 +10,26 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" - env_client "github.com/smartcontractkit/chainlink-testing-framework/k8s/client" + ctfconfig "github.com/smartcontractkit/chainlink-testing-framework/config" + envclient "github.com/smartcontractkit/chainlink-testing-framework/k8s/client" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" + sethutils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + ethcontracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" "github.com/smartcontractkit/chainlink/integration-tests/types" ) var ( - performanceChainlinkResources = map[string]interface{}{ + chainlinkResources = map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ "cpu": "1000m", @@ -41,7 +41,7 @@ var ( }, }, } - performanceDbResources = map[string]interface{}{ + dbResources = map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ "cpu": "1000m", @@ -55,33 +55,6 @@ var ( "stateful": true, "capacity": "10Gi", } - - soakChainlinkResources = map[string]interface{}{ - "resources": map[string]interface{}{ - "requests": map[string]interface{}{ - "cpu": "350m", - "memory": "1Gi", - }, - "limits": map[string]interface{}{ - "cpu": "350m", - "memory": "1Gi", - }, - }, - } - soakDbResources = map[string]interface{}{ - "resources": map[string]interface{}{ - "requests": map[string]interface{}{ - "cpu": "250m", - "memory": "256Mi", - }, - "limits": map[string]interface{}{ - "cpu": "250m", - "memory": "256Mi", - }, - }, - "stateful": true, - "capacity": "10Gi", - } ) type NetworkConfig struct { @@ -115,9 +88,9 @@ func TestAutomationBenchmark(t *testing.T) { benchmarkTestNetwork := getNetworkConfig(&config) l.Info().Str("Namespace", testEnvironment.Cfg.Namespace).Msg("Connected to Keepers Benchmark Environment") - testNetwork := seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, benchmarkNetwork, *testEnvironment) + testNetwork := sethutils.MustReplaceSimulatedNetworkUrlWithK8(l, benchmarkNetwork, *testEnvironment) - chainClient, err := seth_utils.GetChainClientWithConfigFunction(&config, testNetwork, seth_utils.OneEphemeralKeysLiveTestnetAutoFixFn) + chainClient, err := sethutils.GetChainClientWithConfigFunction(&config, testNetwork, sethutils.OneEphemeralKeysLiveTestnetAutoFixFn) require.NoError(t, err, "Error getting Seth client") registryVersions := addRegistry(&config) @@ -173,40 +146,42 @@ func TestAutomationBenchmark(t *testing.T) { keeperBenchmarkTest.Run() } -func addRegistry(config *tc.TestConfig) []eth_contracts.KeeperRegistryVersion { +func addRegistry(config *tc.TestConfig) []ethcontracts.KeeperRegistryVersion { switch *config.Keeper.Common.RegistryToTest { case "1_1": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_1_1} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_1} case "1_2": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_1_2} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_2} case "1_3": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_1_3} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_3} case "2_0": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0} case "2_1": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_1} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_1} case "2_2": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_2} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_2} + case "2_3": + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_3} case "2_0-1_3": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0, eth_contracts.RegistryVersion_1_3} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0, ethcontracts.RegistryVersion_1_3} case "2_1-2_0-1_3": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_1, - eth_contracts.RegistryVersion_2_0, eth_contracts.RegistryVersion_1_3} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_1, + ethcontracts.RegistryVersion_2_0, ethcontracts.RegistryVersion_1_3} case "2_2-2_1": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_2, eth_contracts.RegistryVersion_2_1} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_2, ethcontracts.RegistryVersion_2_1} case "2_0-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_2_0, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_0, *config.Keeper.Common.NumberOfRegistries) case "2_1-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_2_1, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_1, *config.Keeper.Common.NumberOfRegistries) case "2_2-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_2_2, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_2, *config.Keeper.Common.NumberOfRegistries) default: - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0} } } -func repeatRegistries(registryVersion eth_contracts.KeeperRegistryVersion, numberOfRegistries int) []eth_contracts.KeeperRegistryVersion { - repeatedRegistries := make([]eth_contracts.KeeperRegistryVersion, 0) +func repeatRegistries(registryVersion ethcontracts.KeeperRegistryVersion, numberOfRegistries int) []ethcontracts.KeeperRegistryVersion { + repeatedRegistries := make([]ethcontracts.KeeperRegistryVersion, 0) for i := 0; i < numberOfRegistries; i++ { repeatedRegistries = append(repeatedRegistries, registryVersion) } @@ -316,12 +291,8 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc PreventPodEviction: true, }) - dbResources := performanceDbResources - chainlinkResources := performanceChainlinkResources - if strings.Contains(strings.ToLower(strings.Join(keeperTestConfig.GetConfigurationNames(), ",")), "soak") { - chainlinkResources = soakChainlinkResources - dbResources = soakDbResources - } + dbResources := dbResources + chainlinkResources := chainlinkResources // Test can run on simulated, simulated-non-dev, testnets if testNetwork.Name == networks.SimulatedEVMNonDev.Name { @@ -389,10 +360,10 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc // for simulated-nod-dev each CL node gets its own RPC node if testNetwork.Name == networks.SimulatedEVMNonDev.Name { podName := fmt.Sprintf("%s-ethereum-geth:%d", testNetwork.Name, i) - txNodeInternalWs, err := testEnvironment.Fwd.FindPort(podName, "geth", "ws-rpc").As(env_client.RemoteConnection, env_client.WS) + txNodeInternalWs, err := testEnvironment.Fwd.FindPort(podName, "geth", "ws-rpc").As(envclient.RemoteConnection, envclient.WS) require.NoError(t, err, "Error finding WS ports") internalWsURLs = append(internalWsURLs, txNodeInternalWs) - txNodeInternalHttp, err := testEnvironment.Fwd.FindPort(podName, "geth", "http-rpc").As(env_client.RemoteConnection, env_client.HTTP) + txNodeInternalHttp, err := testEnvironment.Fwd.FindPort(podName, "geth", "http-rpc").As(envclient.RemoteConnection, envclient.HTTP) require.NoError(t, err, "Error finding HTTP ports") internalHttpURLs = append(internalHttpURLs, txNodeInternalHttp) // for testnets with more than 1 RPC nodes @@ -412,8 +383,8 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc testNetwork.URLs = []string{internalWsURLs[i]} var overrideFn = func(_ interface{}, target interface{}) { - ctf_config.MustConfigOverrideChainlinkVersion(keeperTestConfig.GetChainlinkImageConfig(), target) - ctf_config.MightConfigOverridePyroscopeKey(keeperTestConfig.GetPyroscopeConfig(), target) + ctfconfig.MustConfigOverrideChainlinkVersion(keeperTestConfig.GetChainlinkImageConfig(), target) + ctfconfig.MightConfigOverridePyroscopeKey(keeperTestConfig.GetPyroscopeConfig(), target) } tomlConfig, err := actions.BuildTOMLNodeConfigForK8s(keeperTestConfig, testNetwork) diff --git a/integration-tests/contracts/contract_models.go b/integration-tests/contracts/contract_models.go index 5983f95c9f5..ea63f1aa4db 100644 --- a/integration-tests/contracts/contract_models.go +++ b/integration-tests/contracts/contract_models.go @@ -224,7 +224,7 @@ type JobByInstance struct { Instance string } -type MockETHLINKFeed interface { +type MockLINKETHFeed interface { Address() string LatestRoundData() (*big.Int, error) LatestRoundDataUpdatedAt() (*big.Int, error) diff --git a/integration-tests/contracts/ethereum_contracts.go b/integration-tests/contracts/ethereum_contracts.go index 5b08c9a9fbf..0d493dff45e 100644 --- a/integration-tests/contracts/ethereum_contracts.go +++ b/integration-tests/contracts/ethereum_contracts.go @@ -1195,19 +1195,19 @@ func (v *EthereumMockETHLINKFeed) LatestRoundDataUpdatedAt() (*big.Int, error) { return data.UpdatedAt, nil } -func DeployMockETHLINKFeed(client *seth.Client, answer *big.Int) (MockETHLINKFeed, error) { +func DeployMockLINKETHFeed(client *seth.Client, answer *big.Int) (MockLINKETHFeed, error) { abi, err := mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.GetAbi() if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to get MockETHLINKFeed ABI: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to get MockLINKETHFeed ABI: %w", err) } - data, err := client.DeployContract(client.NewTXOpts(), "MockETHLINKFeed", *abi, common.FromHex(mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.Bin), answer) + data, err := client.DeployContract(client.NewTXOpts(), "MockLINKETHFeed", *abi, common.FromHex(mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.Bin), answer) if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("MockETHLINKFeed instance deployment have failed: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("MockLINKETHFeed instance deployment have failed: %w", err) } instance, err := mock_ethlink_aggregator_wrapper.NewMockETHLINKAggregator(data.Address, wrappers.MustNewWrappedContractBackend(nil, client)) if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to instantiate MockETHLINKFeed instance: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to instantiate MockLINKETHFeed instance: %w", err) } return &EthereumMockETHLINKFeed{ @@ -1217,17 +1217,17 @@ func DeployMockETHLINKFeed(client *seth.Client, answer *big.Int) (MockETHLINKFee }, nil } -func LoadMockETHLINKFeed(client *seth.Client, address common.Address) (MockETHLINKFeed, error) { +func LoadMockLINKETHFeed(client *seth.Client, address common.Address) (MockLINKETHFeed, error) { abi, err := mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.GetAbi() if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to get MockETHLINKFeed ABI: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to get MockLINKETHFeed ABI: %w", err) } - client.ContractStore.AddABI("MockETHLINKFeed", *abi) - client.ContractStore.AddBIN("MockETHLINKFeed", common.FromHex(mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.Bin)) + client.ContractStore.AddABI("MockLINKETHFeed", *abi) + client.ContractStore.AddBIN("MockLINKETHFeed", common.FromHex(mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.Bin)) instance, err := mock_ethlink_aggregator_wrapper.NewMockETHLINKAggregator(address, wrappers.MustNewWrappedContractBackend(nil, client)) if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to instantiate MockETHLINKFeed instance: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to instantiate MockLINKETHFeed instance: %w", err) } return &EthereumMockETHLINKFeed{ diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index 28fdd15b13e..38aa5c58f0f 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -150,6 +150,7 @@ type KeeperRegistrySettings struct { MaxPerformGas uint32 // max gas allowed for an upkeep within perform FallbackGasPrice *big.Int // gas price used if the gas price feed is stale FallbackLinkPrice *big.Int // LINK price used if the LINK price feed is stale + FallbackNativePrice *big.Int // Native price used if the Native price feed is stale MaxCheckDataSize uint32 MaxPerformDataSize uint32 MaxRevertDataSize uint32 diff --git a/integration-tests/testconfig/keeper/keeper.toml b/integration-tests/testconfig/keeper/keeper.toml index b4a6a3b2c0e..39eae1ea53c 100644 --- a/integration-tests/testconfig/keeper/keeper.toml +++ b/integration-tests/testconfig/keeper/keeper.toml @@ -57,7 +57,21 @@ contract_call_interval = "4s" [Seth] # keeper benchmark running on simulated network requires 100k per node -root_key_funds_buffer = 700_000 +root_key_funds_buffer = 1_000_000 + +[Benchmark.Keeper.Common] +registry_to_test = "2_1" +number_of_registries = 1 +number_of_nodes = 6 +number_of_upkeeps = 1000 +upkeep_gas_limit = 1500000 +check_gas_to_burn = 10000 +perform_gas_to_burn = 1000 +max_perform_gas = 5000000 +block_range = 3600 +block_interval = 60 +forces_single_tx_key = false +delete_jobs_on_end = true [Benchmark.NodeConfig] BaseConfigTOML = """ @@ -95,3 +109,54 @@ HistoryDepth = 100 Mode = 'FixedPrice' LimitDefault = 5_000_000 """ + +[Soak.Keeper.Common] +registry_to_test = "2_1" +number_of_registries = 1 +number_of_nodes = 6 +number_of_upkeeps = 50 +upkeep_gas_limit = 1500000 +check_gas_to_burn = 10000 +perform_gas_to_burn = 1000 +max_perform_gas = 5000000 +block_range = 28800 +block_interval = 300 +forces_single_tx_key = false +delete_jobs_on_end = true + +[Soak.NodeConfig] +BaseConfigTOML = """ +[Feature] +LogPoller = true + +[OCR2] +Enabled = true + +[P2P] +[P2P.V2] +Enabled = true +AnnounceAddresses = ["0.0.0.0:6690"] +ListenAddresses = ["0.0.0.0:6690"] +[Keeper] +TurnLookBack = 0 +[WebServer] +HTTPWriteTimeout = '1h' +""" + +CommonChainConfigTOML = """ +""" + +[Soak.NodeConfig.ChainConfigTOMLByChainID] +# applicable for simulated chain +1337 = """ +FinalityDepth = 50 +LogPollInterval = '1s' +MinIncomingConfirmations = 1 + +[HeadTracker] +HistoryDepth = 100 + +[GasEstimator] +Mode = 'FixedPrice' +LimitDefault = 5_000_000 +""" diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index 4803a5249f0..a3d6c426e4b 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -62,9 +62,11 @@ type KeeperBenchmarkTest struct { chainClient *seth.Client testConfig tt.KeeperBenchmarkTestConfig - linkToken contracts.LinkToken - ethFeed contracts.MockETHLINKFeed - gasFeed contracts.MockGasFeed + linkToken contracts.LinkToken + linkethFeed contracts.MockLINKETHFeed + gasFeed contracts.MockGasFeed + ethusdFeed contracts.MockETHUSDFeed + wrappedNative contracts.WETHToken } // UpkeepConfig dictates details of how the test's upkeep contracts should be called and configured @@ -163,10 +165,10 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.Keep } if common.IsHexAddress(c.EthFeedAddress) { - _, err = contracts.LoadMockETHLINKFeed(k.chainClient, common.HexToAddress(c.EthFeedAddress)) + _, err = contracts.LoadMockLINKETHFeed(k.chainClient, common.HexToAddress(c.EthFeedAddress)) require.NoError(k.t, err, "Loading ETH-Link feed Contract shouldn't fail") } else { - k.ethFeed, err = contracts.DeployMockETHLINKFeed(k.chainClient, big.NewInt(2e18)) + k.linkethFeed, err = contracts.DeployMockLINKETHFeed(k.chainClient, big.NewInt(2e18)) require.NoError(k.t, err, "Deploying mock ETH-Link feed shouldn't fail") } @@ -178,6 +180,11 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.Keep require.NoError(k.t, err, "Deploying mock gas feed shouldn't fail") } + k.ethusdFeed, err = contracts.DeployMockETHUSDFeed(k.chainClient, big.NewInt(200000000000)) + require.NoError(k.t, err, "Deploying mock ETH-USD feed shouldn't fail") + k.wrappedNative, err = contracts.DeployWETHTokenContract(k.log, k.chainClient) + require.NoError(k.t, err, "Deploying WETH Token Contract shouldn't fail") + for index := range inputs.RegistryVersions { k.log.Info().Int("Index", index).Msg("Starting Test Setup") k.DeployBenchmarkKeeperContracts(index) @@ -240,15 +247,14 @@ func (k *KeeperBenchmarkTest) Run() { txKeyId = 0 } kr := k.keeperRegistries[rIndex] - // TODO: need to add the LINK, WETH and WETH/USD feed to support v23 ocrConfig, err := actions.BuildAutoOCR2ConfigVarsWithKeyIndex( - k.t, nodesWithoutBootstrap, *inputs.KeeperRegistrySettings, kr.Address(), k.Inputs.DeltaStage, txKeyId, common.Address{}, kr.ChainModuleAddress(), kr.ReorgProtectionEnabled(), nil, nil, nil, + k.t, nodesWithoutBootstrap, *inputs.KeeperRegistrySettings, kr.Address(), k.Inputs.DeltaStage, txKeyId, common.Address{}, kr.ChainModuleAddress(), kr.ReorgProtectionEnabled(), k.linkToken, k.wrappedNative, k.ethusdFeed, ) require.NoError(k.t, err, "Building OCR config shouldn't fail") rv := inputs.RegistryVersions[rIndex] // Send keeper jobs to registry and chainlink nodes - if rv == ethereum.RegistryVersion_2_0 || rv == ethereum.RegistryVersion_2_1 || rv == ethereum.RegistryVersion_2_2 { + if rv >= ethereum.RegistryVersion_2_0 { actions.CreateOCRKeeperJobs(k.t, k.chainlinkNodes, kr.Address(), k.chainClient.ChainID, txKeyId, rv) if rv == ethereum.RegistryVersion_2_0 { err = kr.SetConfig(*inputs.KeeperRegistrySettings, ocrConfig) @@ -708,7 +714,7 @@ func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int) { registry, err = contracts.DeployKeeperRegistry(k.chainClient, &contracts.KeeperRegistryOpts{ RegistryVersion: registryVersion, LinkAddr: k.linkToken.Address(), - ETHFeedAddr: k.ethFeed.Address(), + ETHFeedAddr: k.linkethFeed.Address(), GasFeedAddr: k.gasFeed.Address(), TranscoderAddr: actions.ZeroAddress.Hex(), RegistrarAddr: actions.ZeroAddress.Hex(), @@ -731,13 +737,13 @@ func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int) { require.NoError(k.t, err, "Funding keeper registrar contract shouldn't fail") } else { // OCR automation - v2.X registry, registrar = actions.DeployAutoOCRRegistryAndRegistrar( - k.t, k.chainClient, registryVersion, *k.Inputs.KeeperRegistrySettings, k.linkToken, nil, nil, + k.t, k.chainClient, registryVersion, *k.Inputs.KeeperRegistrySettings, k.linkToken, k.wrappedNative, k.ethusdFeed, ) // Fund the registry with LINK err := k.linkToken.Transfer(registry.Address(), big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(k.Inputs.Upkeeps.NumberOfUpkeeps)))) require.NoError(k.t, err, "Funding keeper registry contract shouldn't fail") - ocrConfig, err := actions.BuildAutoOCR2ConfigVars(k.t, k.chainlinkNodes[1:], *k.Inputs.KeeperRegistrySettings, registrar.Address(), k.Inputs.DeltaStage, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled(), nil, nil, nil) + ocrConfig, err := actions.BuildAutoOCR2ConfigVars(k.t, k.chainlinkNodes[1:], *k.Inputs.KeeperRegistrySettings, registrar.Address(), k.Inputs.DeltaStage, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled(), k.linkToken, k.wrappedNative, k.ethusdFeed) require.NoError(k.t, err, "Building OCR config shouldn't fail") k.log.Debug().Interface("KeeperRegistrySettings", *k.Inputs.KeeperRegistrySettings).Interface("OCRConfig", ocrConfig).Msg("Config") require.NoError(k.t, err, "Error building OCR config vars")