Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultiOffRamp - size optimizations #991

Merged
merged 27 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8529803
feat: remove in-depth message validations in multi-offramp
elatoskinas Jun 10, 2024
fc28d0f
feat: split out isCursed check to separate function
elatoskinas Jun 10, 2024
7ab03eb
feat: remove global pause from multi-offramp
elatoskinas Jun 10, 2024
883043e
perf: reduce number of optimizer runs for multi-offramp
elatoskinas Jun 11, 2024
939f505
feat: move forked chain check to common internal function
elatoskinas Jun 11, 2024
7c6944b
feat: split StaticConfig and DynamicConfig events
elatoskinas Jun 11, 2024
de7bf41
perf: bump up multi-offramp optimizer runs
elatoskinas Jun 11, 2024
af9727a
feat: remove uniqueReports from MultiOCR3
elatoskinas Jun 11, 2024
14231e1
feat: convert epochAndRound to sequenceNumber for OCR3
elatoskinas Jun 11, 2024
5b9b3cf
feat: combine source chain config is enabled check into internal func…
elatoskinas Jun 11, 2024
2db2393
feat: separate zero address check to internal function
elatoskinas Jun 11, 2024
64eab8d
fix: test fixes and compiler runs change
elatoskinas Jun 13, 2024
6252749
perf: inline reporting functions for commit & exec
elatoskinas Jun 13, 2024
d1de42d
feat: split out bitmap fetching to internal function
elatoskinas Jun 13, 2024
ac8dd97
fix: no returns on empty roots commit report
elatoskinas Jun 13, 2024
dbd2a6e
feat: apply internal function change for releaseOrMintTokens
elatoskinas Jun 13, 2024
a9ebe66
chore: generate wrappers, snapshot, changeset
elatoskinas Jun 13, 2024
1667bb5
Merge branch 'ccip-develop' into feat/merged-ramp-size-optimizations
elatoskinas Jun 20, 2024
d37b2d3
refactor: split out functions for readability
elatoskinas Jun 20, 2024
feaa3e6
Merge branch 'ccip-develop' into feat/merged-ramp-size-optimizations
elatoskinas Jun 20, 2024
a630eee
refactor: remove common zero address check
elatoskinas Jun 20, 2024
61ff676
chore: re-gen snapshots, wrappers and reduce optimizer runs
elatoskinas Jun 20, 2024
c42f37d
Merge branch 'ccip-develop' into feat/merged-ramp-size-optimizations
elatoskinas Jun 20, 2024
b298761
fix: revert offramp optimizer runs
elatoskinas Jun 20, 2024
49d3681
Merge branch 'ccip-develop' into feat/merged-ramp-size-optimizations
elatoskinas Jun 21, 2024
c7d74a0
style: renaming and variable placement
elatoskinas Jun 21, 2024
bc9743a
chore: update gas snapshots and wrappers
elatoskinas Jun 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions contracts/.changeset/gentle-spoons-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/contracts-ccip': minor
---

#changed MultiOffRamp contract size optimizations
347 changes: 171 additions & 176 deletions contracts/gas-snapshots/ccip.gas-snapshot

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion contracts/scripts/native_solc_compile_all_ccip
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SOLC_VERSION="0.8.24"
OPTIMIZE_RUNS=26000
OPTIMIZE_RUNS_OFFRAMP=19500
OPTIMIZE_RUNS_ONRAMP=3600
OPTIMIZE_RUNS_MULTI_OFFRAMP=2400


SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
Expand All @@ -27,10 +28,14 @@ compileContract () {
local optimize_runs=$OPTIMIZE_RUNS

case $1 in
"ccip/offRamp/EVM2EVMOffRamp.sol" | "ccip/offRamp/EVM2EVMMultiOffRamp.sol")
"ccip/offRamp/EVM2EVMOffRamp.sol")
echo "OffRamp uses $OPTIMIZE_RUNS_OFFRAMP optimizer runs."
optimize_runs=$OPTIMIZE_RUNS_OFFRAMP
;;
"ccip/offRamp/EVM2EVMMultiOffRamp.sol")
echo "MultiOffRamp uses $OPTIMIZE_RUNS_MULTI_OFFRAMP optimizer runs."
optimize_runs=$OPTIMIZE_RUNS_MULTI_OFFRAMP
;;
"ccip/onRamp/EVM2EVMMultiOnRamp.sol" | "ccip/onRamp/EVM2EVMOnRamp.sol")
echo "OnRamp uses $OPTIMIZE_RUNS_ONRAMP optimizer runs."
optimize_runs=$OPTIMIZE_RUNS_ONRAMP
Expand Down
5 changes: 5 additions & 0 deletions contracts/src/v0.8/ccip/libraries/Internal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {Client} from "./Client.sol";
// Library for CCIP internal definitions common to multiple contracts.
library Internal {
error InvalidEVMAddress(bytes encodedAddress);
error ZeroAddressNotAllowed();

/// @dev The minimum amount of gas to perform the call with exact gas.
/// We include this in the offramp so that we can redeploy to adjust it
Expand Down Expand Up @@ -181,6 +182,10 @@ library Internal {
return address(uint160(encodedAddress));
}

function _validateNonZeroAddress(address inputAddress) internal pure {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly not super fan of this as it makes the calls with multiple zero checks pretty unreadable

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this. The overhead is 0.056KB without the optimization, but I agree regarding the readability

if (inputAddress == address(0)) revert ZeroAddressNotAllowed();
}

/// @notice Enum listing the possible message execution states within
/// the offRamp contract.
/// UNTOUCHED never executed
Expand Down
30 changes: 10 additions & 20 deletions contracts/src/v0.8/ccip/ocr/MultiOCR3Base.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pragma solidity ^0.8.0;
import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol";
import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";

// TODO: consider splitting configs & verification logic off to auth library (if size is prohibitive)
/// @notice Onchain verification of reports from the offchain reporting protocol
/// with multiple OCR plugin support.
abstract contract MultiOCR3Base is ITypeAndVersion, OwnerIsCreator {
Expand Down Expand Up @@ -50,7 +49,6 @@ abstract contract MultiOCR3Base is ITypeAndVersion, OwnerIsCreator {
bytes32 configDigest;
uint8 F; // ──────────────────────────────╮ maximum number of faulty/dishonest oracles the system can tolerate
uint8 n; // │ number of signers / transmitters
bool uniqueReports; // │ if true, the reports should be unique
bool isSignatureVerificationEnabled; // ──╯ if true, requires signers and verifies signatures on transmission verification
}

Expand Down Expand Up @@ -85,7 +83,6 @@ abstract contract MultiOCR3Base is ITypeAndVersion, OwnerIsCreator {
bytes32 configDigest; // Config digest to update to
uint8 ocrPluginType; // ──────────────────╮ OCR plugin type to update config for
uint8 F; // │ maximum number of faulty/dishonest oracles
bool uniqueReports; // │ if true, the reports should be unique
bool isSignatureVerificationEnabled; // ──╯ if true, requires signers and verifies signatures on transmission verification
address[] signers; // signing address of each oracle
address[] transmitters; // transmission address of each oracle (i.e. the address the oracle actually sends transactions to the contract from)
Expand Down Expand Up @@ -143,12 +140,8 @@ abstract contract MultiOCR3Base is ITypeAndVersion, OwnerIsCreator {

// If F is 0, then the config is not yet set
if (configInfo.F == 0) {
configInfo.uniqueReports = ocrConfigArgs.uniqueReports;
configInfo.isSignatureVerificationEnabled = ocrConfigArgs.isSignatureVerificationEnabled;
} else if (
configInfo.uniqueReports != ocrConfigArgs.uniqueReports
|| configInfo.isSignatureVerificationEnabled != ocrConfigArgs.isSignatureVerificationEnabled
) {
} else if (configInfo.isSignatureVerificationEnabled != ocrConfigArgs.isSignatureVerificationEnabled) {
revert StaticConfigCannotBeChanged(ocrPluginType);
}

Expand Down Expand Up @@ -228,15 +221,13 @@ abstract contract MultiOCR3Base is ITypeAndVersion, OwnerIsCreator {
// TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
bytes32[3] calldata reportContext,
bytes calldata report,
// TODO: revisit trade-off - converting this to calldata and using one CONSTANT_LENGTH_COMPONENT
// decreases contract size by ~220B, decreasees commit gas usage by ~400 gas, but increases exec gas usage by ~3600 gas
bytes32[] memory rs,
bytes32[] memory ss,
bytes32 rawVs // signatures
) internal {
// reportContext consists of:
// reportContext[0]: ConfigDigest
// reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round
// reportContext[1]: 24 byte padding, 8 byte sequence number
// reportContext[2]: ExtraHash
ConfigInfo memory configInfo = s_ocrConfigs[ocrPluginType].configInfo;
bytes32 configDigest = reportContext[0];
Expand All @@ -259,7 +250,7 @@ abstract contract MultiOCR3Base is ITypeAndVersion, OwnerIsCreator {
// If the cached chainID at time of deployment doesn't match the current chainID, we reject all signed reports.
// This avoids a (rare) scenario where chain A forks into chain A and A', A' still has configDigest
// calculated from chain A and so OCR reports will be valid on both forks.
if (i_chainID != block.chainid) revert ForkedChain(i_chainID, block.chainid);
_whenChainNotForked();

// Scoping this reduces stack pressure and gas usage
{
Expand All @@ -278,21 +269,15 @@ abstract contract MultiOCR3Base is ITypeAndVersion, OwnerIsCreator {
if (configInfo.isSignatureVerificationEnabled) {
// Scoping to reduce stack pressure
{
uint256 expectedNumSignatures;
if (configInfo.uniqueReports) {
expectedNumSignatures = (configInfo.n + configInfo.F) / 2 + 1;
} else {
expectedNumSignatures = configInfo.F + 1;
}
if (rs.length != expectedNumSignatures) revert WrongNumberOfSignatures();
if (rs.length != configInfo.F + 1) revert WrongNumberOfSignatures();
if (rs.length != ss.length) revert SignaturesOutOfRegistration();
}

bytes32 h = keccak256(abi.encodePacked(keccak256(report), reportContext));
_verifySignatures(ocrPluginType, h, rs, ss, rawVs);
}

emit Transmitted(ocrPluginType, configDigest, uint32(uint256(reportContext[1]) >> 8));
emit Transmitted(ocrPluginType, configDigest, uint64(uint256(reportContext[1])));
}

/// @notice verifies the signatures of a hashed report value for one OCR plugin type
Expand Down Expand Up @@ -324,6 +309,11 @@ abstract contract MultiOCR3Base is ITypeAndVersion, OwnerIsCreator {
}
}

/// @notice Validates that the chain ID has not diverged after deployment. Reverts if the chain IDs do not match
function _whenChainNotForked() internal view {
if (i_chainID != block.chainid) revert ForkedChain(i_chainID, block.chainid);
}

/// @notice information about current offchain reporting protocol configuration
/// @param ocrPluginType OCR plugin type to return config details for
/// @return ocrConfig OCR config for the plugin type
Expand Down
Loading
Loading