Skip to content

Commit 44cd5fc

Browse files
[eth] more update/parsePriceFeed optimizations (#886)
* perf(ethereum): optimize gas & bytesize by changing some function parameters to use bytes memory change parseWormholeMerkleHeaderNumUpdates & parsePriceFeedMessage to use bytes memory instead of bytes calldata for function parameters * perf(ethereum): more optimizations use bytes memory for more functions, change parsePriceFeedMessage to use offset instead of copying a slice * [eth] Add unsafe calldata bytes lib (#888) * Copy UnsafeBytesLib to UnsafeCalldataBytesLib * [eth] Add UnsafeCalldataBytesLib and update code --------- Co-authored-by: Ali Behjati <[email protected]>
1 parent bc338cc commit 44cd5fc

File tree

4 files changed

+224
-67
lines changed

4 files changed

+224
-67
lines changed

target_chains/ethereum/contracts/contracts/libraries/MerkleTree.sol

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
pragma solidity ^0.8.0;
44

5-
import "./external/UnsafeBytesLib.sol";
5+
import "./external/UnsafeCalldataBytesLib.sol";
66

77
/**
88
* @dev This library provides methods to construct and verify Merkle Tree proofs efficiently.
@@ -36,7 +36,7 @@ library MerkleTree {
3636
return hash(abi.encodePacked(MERKLE_NODE_PREFIX, childA, childB));
3737
}
3838

39-
/// @notice Verify Merkle Tree proof for given leaf data.
39+
/// @notice Verify Merkle Tree proof for given leaf data based on data on memory.
4040
/// @dev To optimize gas usage, this method doesn't take the proof as a bytes array
4141
/// but rather takes the encoded proof and the offset of the proof in the
4242
/// encoded proof array possibly containing multiple proofs. Also, the method
@@ -45,20 +45,23 @@ library MerkleTree {
4545
/// that the `encodedProof` is long enough to contain the proof and the
4646
/// `proofOffset` is not out of bound.
4747
function isProofValid(
48-
bytes memory encodedProof,
48+
bytes calldata encodedProof,
4949
uint proofOffset,
5050
bytes20 root,
51-
bytes memory leafData
51+
bytes calldata leafData
5252
) internal pure returns (bool valid, uint endOffset) {
5353
unchecked {
5454
bytes20 currentDigest = MerkleTree.leafHash(leafData);
5555

56-
uint8 proofSize = UnsafeBytesLib.toUint8(encodedProof, proofOffset);
56+
uint8 proofSize = UnsafeCalldataBytesLib.toUint8(
57+
encodedProof,
58+
proofOffset
59+
);
5760
proofOffset += 1;
5861

5962
for (uint i = 0; i < proofSize; i++) {
6063
bytes20 siblingDigest = bytes20(
61-
UnsafeBytesLib.toAddress(encodedProof, proofOffset)
64+
UnsafeCalldataBytesLib.toAddress(encodedProof, proofOffset)
6265
);
6366
proofOffset += 20;
6467

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// SPDX-License-Identifier: Unlicense
2+
/*
3+
* @title Solidity Bytes Arrays Utils
4+
* @author Gonçalo Sá <[email protected]>
5+
*
6+
* @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
7+
* The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
8+
*
9+
* @notice This is the **unsafe** version of BytesLib which removed all the checks (out of bound, ...)
10+
* to be more gas efficient.
11+
*/
12+
pragma solidity >=0.8.0 <0.9.0;
13+
14+
library UnsafeCalldataBytesLib {
15+
function slice(
16+
bytes calldata _bytes,
17+
uint256 _start,
18+
uint256 _length
19+
) internal pure returns (bytes calldata) {
20+
return _bytes[_start:_start + _length];
21+
}
22+
23+
function toAddress(
24+
bytes calldata _bytes,
25+
uint256 _start
26+
) internal pure returns (address) {
27+
address tempAddress;
28+
29+
assembly {
30+
tempAddress := shr(96, calldataload(add(_bytes.offset, _start)))
31+
}
32+
33+
return tempAddress;
34+
}
35+
36+
function toUint8(
37+
bytes calldata _bytes,
38+
uint256 _start
39+
) internal pure returns (uint8) {
40+
uint8 tempUint;
41+
42+
assembly {
43+
tempUint := shr(248, calldataload(add(_bytes.offset, _start)))
44+
}
45+
46+
return tempUint;
47+
}
48+
49+
function toUint16(
50+
bytes calldata _bytes,
51+
uint256 _start
52+
) internal pure returns (uint16) {
53+
uint16 tempUint;
54+
55+
assembly {
56+
tempUint := shr(240, calldataload(add(_bytes.offset, _start)))
57+
}
58+
59+
return tempUint;
60+
}
61+
62+
function toUint32(
63+
bytes calldata _bytes,
64+
uint256 _start
65+
) internal pure returns (uint32) {
66+
uint32 tempUint;
67+
68+
assembly {
69+
tempUint := shr(224, calldataload(add(_bytes.offset, _start)))
70+
}
71+
72+
return tempUint;
73+
}
74+
75+
function toUint64(
76+
bytes calldata _bytes,
77+
uint256 _start
78+
) internal pure returns (uint64) {
79+
uint64 tempUint;
80+
81+
assembly {
82+
tempUint := shr(192, calldataload(add(_bytes.offset, _start)))
83+
}
84+
85+
return tempUint;
86+
}
87+
88+
function toUint96(
89+
bytes calldata _bytes,
90+
uint256 _start
91+
) internal pure returns (uint96) {
92+
uint96 tempUint;
93+
94+
assembly {
95+
tempUint := shr(160, calldataload(add(_bytes.offset, _start)))
96+
}
97+
98+
return tempUint;
99+
}
100+
101+
function toUint128(
102+
bytes calldata _bytes,
103+
uint256 _start
104+
) internal pure returns (uint128) {
105+
uint128 tempUint;
106+
107+
assembly {
108+
tempUint := shr(128, calldataload(add(_bytes.offset, _start)))
109+
}
110+
111+
return tempUint;
112+
}
113+
114+
function toUint256(
115+
bytes calldata _bytes,
116+
uint256 _start
117+
) internal pure returns (uint256) {
118+
uint256 tempUint;
119+
120+
assembly {
121+
tempUint := calldataload(add(_bytes.offset, _start))
122+
}
123+
124+
return tempUint;
125+
}
126+
127+
function toBytes32(
128+
bytes calldata _bytes,
129+
uint256 _start
130+
) internal pure returns (bytes32) {
131+
bytes32 tempBytes32;
132+
133+
assembly {
134+
tempBytes32 := calldataload(add(_bytes.offset, _start))
135+
}
136+
137+
return tempBytes32;
138+
}
139+
}

target_chains/ethereum/contracts/contracts/pyth/Pyth.sol

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ abstract contract Pyth is
107107
for (uint i = 0; i < updateData.length; i++) {
108108
if (
109109
updateData[i].length > 4 &&
110-
UnsafeBytesLib.toUint32(updateData[i], 0) == ACCUMULATOR_MAGIC
110+
UnsafeCalldataBytesLib.toUint32(updateData[i], 0) ==
111+
ACCUMULATOR_MAGIC
111112
) {
112113
(
113114
uint offset,
@@ -447,13 +448,16 @@ abstract contract Pyth is
447448
override
448449
returns (PythStructs.PriceFeed[] memory priceFeeds)
449450
{
451+
{
452+
uint requiredFee = getUpdateFee(updateData);
453+
if (msg.value < requiredFee) revert PythErrors.InsufficientFee();
454+
}
450455
unchecked {
451-
uint totalNumUpdates = 0;
452456
priceFeeds = new PythStructs.PriceFeed[](priceIds.length);
453457
for (uint i = 0; i < updateData.length; i++) {
454458
if (
455459
updateData[i].length > 4 &&
456-
UnsafeBytesLib.toUint32(updateData[i], 0) ==
460+
UnsafeCalldataBytesLib.toUint32(updateData[i], 0) ==
457461
ACCUMULATOR_MAGIC
458462
) {
459463
uint offset;
@@ -473,7 +477,7 @@ abstract contract Pyth is
473477

474478
bytes20 digest;
475479
uint8 numUpdates;
476-
bytes memory encoded;
480+
bytes calldata encoded;
477481
(
478482
offset,
479483
digest,
@@ -525,7 +529,6 @@ abstract contract Pyth is
525529
}
526530
}
527531
}
528-
totalNumUpdates += numUpdates;
529532
if (offset != encoded.length)
530533
revert PythErrors.InvalidUpdateData();
531534
} else {
@@ -600,7 +603,6 @@ abstract contract Pyth is
600603

601604
index += attestationSize;
602605
}
603-
totalNumUpdates += 1;
604606
}
605607
}
606608

@@ -609,12 +611,6 @@ abstract contract Pyth is
609611
revert PythErrors.PriceFeedNotFoundWithinRange();
610612
}
611613
}
612-
613-
{
614-
uint requiredFee = getTotalFee(totalNumUpdates);
615-
if (msg.value < requiredFee)
616-
revert PythErrors.InsufficientFee();
617-
}
618614
}
619615
}
620616

0 commit comments

Comments
 (0)