@@ -85,6 +85,24 @@ contract PythWormholeMerkleAccumulatorTest is
8585 assertEq (emaPrice.publishTime, priceFeed.emaPrice.publishTime);
8686 }
8787
88+ function assertPriceFeedEqual (
89+ PythStructs.PriceFeed memory priceFeed1 ,
90+ PythStructs.PriceFeed memory priceFeed2
91+ ) internal {
92+ assertEq (priceFeed1.id, priceFeed2.id);
93+ assertEq (priceFeed1.price.price, priceFeed2.price.price);
94+ assertEq (priceFeed1.price.conf, priceFeed2.price.conf);
95+ assertEq (priceFeed1.price.expo, priceFeed2.price.expo);
96+ assertEq (priceFeed1.price.publishTime, priceFeed2.price.publishTime);
97+ assertEq (priceFeed1.emaPrice.price, priceFeed2.emaPrice.price);
98+ assertEq (priceFeed1.emaPrice.conf, priceFeed2.emaPrice.conf);
99+ assertEq (priceFeed1.emaPrice.expo, priceFeed2.emaPrice.expo);
100+ assertEq (
101+ priceFeed1.emaPrice.publishTime,
102+ priceFeed2.emaPrice.publishTime
103+ );
104+ }
105+
88106 function generateRandomPriceFeedMessage (
89107 uint numPriceFeeds
90108 ) internal returns (PriceFeedMessage[] memory priceFeedMessages ) {
@@ -120,6 +138,36 @@ contract PythWormholeMerkleAccumulatorTest is
120138 updateFee = pyth.getUpdateFee (updateData);
121139 }
122140
141+ /// @notice This method creates a forward compatible wormhole update data by using a newer minor version,
142+ /// setting a trailing header size and generating additional trailing header data of size `trailingHeaderSize`
143+ function createFowardCompatibleWormholeMerkleUpdateData (
144+ PriceFeedMessage[] memory priceFeedMessages ,
145+ uint8 minorVersion ,
146+ uint8 trailingHeaderSize
147+ ) internal returns (bytes [] memory updateData , uint updateFee ) {
148+ updateData = new bytes [](1 );
149+
150+ uint8 depth = 0 ;
151+ while ((1 << depth) < priceFeedMessages.length ) {
152+ depth++ ;
153+ }
154+
155+ depth += getRandUint8 () % 3 ;
156+ bytes memory trailingHeaderData = new bytes (uint8 (0 ));
157+ for (uint i = 0 ; i < trailingHeaderSize; i++ ) {
158+ trailingHeaderData = abi.encodePacked (trailingHeaderData, uint8 (i));
159+ }
160+ updateData[0 ] = generateForwardCompatibleWhMerkleUpdate (
161+ priceFeedMessages,
162+ depth,
163+ 1 ,
164+ minorVersion,
165+ trailingHeaderData
166+ );
167+
168+ updateFee = pyth.getUpdateFee (updateData);
169+ }
170+
123171 /// Testing update price feeds method using wormhole merkle update type.
124172 function testUpdatePriceFeedWithWormholeMerkleWorks (uint seed ) public {
125173 setRandSeed (seed);
@@ -947,6 +995,77 @@ contract PythWormholeMerkleAccumulatorTest is
947995 assertEq (updateFee, SINGLE_UPDATE_FEE_IN_WEI * numPriceFeeds);
948996 }
949997
950- //TODO: add some tests of forward compatibility.
951- // I.e., create a message where each part that can be expanded in size is expanded and make sure that parsing still works
998+ function testParsePriceFeedUpdatesWithWhMerkleUpdateWorksForForwardCompatibility ()
999+ public
1000+ {
1001+ uint numPriceFeeds = (getRandUint () % 10 ) + 1 ;
1002+ PriceFeedMessage[]
1003+ memory priceFeedMessages = generateRandomPriceFeedMessage (
1004+ numPriceFeeds
1005+ );
1006+ (
1007+ bytes [] memory updateData ,
1008+ uint updateFee
1009+ ) = createWormholeMerkleUpdateData (priceFeedMessages);
1010+
1011+ bytes32 [] memory priceIds = new bytes32 [](numPriceFeeds);
1012+ for (uint i = 0 ; i < numPriceFeeds; i++ ) {
1013+ priceIds[i] = priceFeedMessages[i].priceId;
1014+ }
1015+ PythStructs.PriceFeed[] memory priceFeeds = pyth.parsePriceFeedUpdates {
1016+ value: updateFee
1017+ }(updateData, priceIds, 0 , MAX_UINT64);
1018+ uint8 futureMinorVersion = uint8 (2 );
1019+ uint8 futureTrailingHeaderSize = uint8 (20 );
1020+ (
1021+ bytes [] memory updateDataFromFuture ,
1022+ uint updateFeeFromFuture
1023+ ) = createFowardCompatibleWormholeMerkleUpdateData (
1024+ priceFeedMessages,
1025+ futureMinorVersion,
1026+ futureTrailingHeaderSize
1027+ );
1028+
1029+ PythStructs.PriceFeed[] memory priceFeedsFromFutureUpdateData = pyth
1030+ .parsePriceFeedUpdates {value: updateFeeFromFuture}(
1031+ updateDataFromFuture,
1032+ priceIds,
1033+ 0 ,
1034+ MAX_UINT64
1035+ );
1036+ assertEq (updateFee, updateFeeFromFuture);
1037+
1038+ for (uint i = 0 ; i < priceFeeds.length ; i++ ) {
1039+ assertPriceFeedEqual (
1040+ priceFeeds[i],
1041+ priceFeedsFromFutureUpdateData[i]
1042+ );
1043+ }
1044+ }
1045+
1046+ function testUpdatePriceFeedUpdatesWithWhMerkleUpdateWorksForForwardCompatibility ()
1047+ public
1048+ {
1049+ uint numPriceFeeds = (getRandUint () % 10 ) + 1 ;
1050+ PriceFeedMessage[]
1051+ memory priceFeedMessages = generateRandomPriceFeedMessage (
1052+ numPriceFeeds
1053+ );
1054+ uint8 futureMinorVersion = uint8 (2 );
1055+ uint8 futureTrailingHeaderSize = uint8 (20 );
1056+ (
1057+ bytes [] memory forwardCompatibleUpdateData ,
1058+ uint updateFee
1059+ ) = createFowardCompatibleWormholeMerkleUpdateData (
1060+ priceFeedMessages,
1061+ futureMinorVersion,
1062+ futureTrailingHeaderSize
1063+ );
1064+
1065+ pyth.updatePriceFeeds {value: updateFee}(forwardCompatibleUpdateData);
1066+
1067+ for (uint i = 0 ; i < priceFeedMessages.length ; i++ ) {
1068+ assertPriceFeedMessageStored (priceFeedMessages[i]);
1069+ }
1070+ }
9521071}
0 commit comments