Aditya Arora 9 сар өмнө
parent
commit
507b5caf91

+ 83 - 9
target_chains/ethereum/contracts/contracts/pyth/Pyth.sol

@@ -311,15 +311,16 @@ abstract contract Pyth is
     function parseTwapPriceFeedUpdates(
         bytes[2][] calldata updateData,
         bytes32[] calldata priceIds,
-        unit8 windowSize
+        uint8 windowSize
     )
         external
         payable
-        override
         returns (PythStructs.TwapPriceFeed[] memory twapPriceFeeds)
     {
         {
-            revert(updateData.length != 2, PythErrors.InvalidUpdateData());
+            if (updateData.length != 2) {
+                revert PythErrors.InvalidUpdateData();
+            }
             uint requiredFee = getUpdateFee(updateData[0]);
 
             // Check if the two updateData contains the same number of priceUpdates
@@ -330,6 +331,7 @@ abstract contract Pyth is
         }
 
         // Parse the updateData
+        unchecked {
         twapPriceFeeds = new PythStructs.TwapPriceFeed[](priceIds.length);
         for (uint i = 0; i < updateData[0].length; i++) {
             if (
@@ -341,6 +343,7 @@ abstract contract Pyth is
                     ACCUMULATOR_MAGIC)
             ) {
                 // Parse the accumulator update
+                // I believe the offset will be same for both updateData[0][i] and updateData[1][i]
                 uint offsetFirst;
                 uint offsetSecond;
                 {
@@ -364,7 +367,7 @@ abstract contract Pyth is
                 (
                     offsetFirst,
                     digestFirst,
-                    numUpdates,
+                    numUpdatesFirst,
                     encodedFirst
                 ) = extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedFromAccumulatorUpdate(
                     updateData[0][i],
@@ -373,12 +376,12 @@ abstract contract Pyth is
                 (
                     offsetSecond,
                     digestSecond,
-                    numUpdates,
+                    numUpdatesSecond,
                     encodedSecond
                 ) = extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedFromAccumulatorUpdate(
                     updateData[1][i],
                     offsetSecond);
-
+                // I believe this check is redundant
                 if (numUpdatesFirst != numUpdatesSecond) {
                     revert PythErrors.InvalidUpdateData();
                 }
@@ -389,15 +392,86 @@ abstract contract Pyth is
                     bytes32 priceIdFirst;
                     bytes32 priceIdSecond;
 
-                    (offsetFirst, twapPriceInfoFirst, priceIdFirst) = extractPriceInfoFromMerkleProof(digestFirst, encodedFirst, offsetFirst);
-                    (offsetSecond, twapPriceInfoSecond, priceIdSecond) = extractPriceInfoFromMerkleProof(digestSecond, encodedSecond, offsetSecond);
-                }
+                    (offsetFirst, twapPriceInfoFirst, priceIdFirst) = extractTwapPriceInfoFromMerkleProof(digestFirst, encodedFirst, offsetFirst);
+                    (offsetSecond, twapPriceInfoSecond, priceIdSecond) = extractTwapPriceInfoFromMerkleProof(digestSecond, encodedSecond, offsetSecond);
 
+                    require(priceIdFirst == priceIdSecond, PythErrors.InvalidTwapUpdateDataSet());
                 
+                    // No Updates here.
+                    // check whether caller requested for this data
+                    uint k = findIndexOfPriceId(priceIds, priceIdFirst);
+                    if (k == priceIds.length || twapPriceFeeds[k].id != 0) {
+                        continue;
+                    }
+
+                    // Since we have already validated the twap price info, we can directly use it
+                    validateTwapPriceInfo(twapPriceInfoFirst, twapPriceInfoSecond);
+
+                    // Now we will calcualte the cumulative price and cumulative conf
+                    // for the first and second priceId
+                    // I believe the cumulative price and cumulative conf will be same for both priceIdFirst and priceIdSecond
+                    // because they are both from the same accumulator update
+                    uint64 slotDiff = twapPriceInfoSecond.publishSlot - twapPriceInfoFirst.publishSlot;
+                    int128 priceDiff = twapPriceInfoSecond.price - twapPriceInfoFirst.price;
+                    uint128 confDiff = twapPriceInfoSecond.conf - twapPriceInfoFirst.conf;
+
+                    // Now we will calculate the twap price and twap conf
+                    // for the first and second priceId
+                    int128 twapPrice = priceDiff / slotDiff;
+                    uint128 twapConf = confDiff / slotDiff;
+
+                    twapPriceFeeds[k].id = priceIdFirst;
+                    twapPriceFeeds[k].twap.price = twapPrice;
+                    twapPriceFeeds[k].twap.conf = twapConf;
+                    twapPriceFeeds[k].twap.expo = twapPriceInfoFirst.expo;
+                    twapPriceFeeds[k].twap.publishTime = twapPriceInfoSecond.publishTime;
+                    twapPriceFeeds[k].startTime = twapPriceInfoFirst.publishTime;
+                    twapPriceFeeds[k].endTime = twapPriceInfoSecond.publishTime;
+                    //TODO: Calculate the downSlotRatio
+                }
+            if (offsetFirst != encodedFirst.length) {
+                    revert PythErrors.InvalidTwapUpdateData();
+                }
+                if (offsetSecond != encodedSecond.length) {
+                    revert PythErrors.InvalidTwapUpdateData();
+                }
+                if (offsetFirst != offsetSecond) {
+                    revert PythErrors.InvalidTwapUpdateData();
+                } else {
+                    revert PythErrors.InvalidUpdateData();
+                }
             } else {
                 revert PythErrors.InvalidUpdateData();
             }
         }
+
+            for (uint k = 0; k < priceIds.length; k++) {
+                if (twapPriceFeeds[k].id == 0) {
+                    revert PythErrors.PriceFeedNotFoundWithinRange();
+                }
+            }
+        }
+    }
+
+    function validateTwapPriceInfo(
+        PythInternalStructs.TwapPriceInfo memory twapPriceInfoFirst,
+        PythInternalStructs.TwapPriceInfo memory twapPriceInfoSecond
+    ) private pure {
+        if (twapPriceInfoFirst.expo != twapPriceInfoSecond.expo) {
+            revert PythErrors.InvalidTwapUpdateDataSet();
+        }
+        if (twapPriceInfoFirst.publishSlot > twapPriceInfoSecond.publishSlot) {
+            revert PythErrors.InvalidTwapUpdateDataSet();
+        }
+        if (twapPriceInfoFirst.prevPublishTime > twapPriceInfoFirst.publishTime) {
+            revert PythErrors.InvalidTwapUpdateData();
+        }
+        if (twapPriceInfoSecond.prevPublishTime > twapPriceInfoSecond.publishTime) {
+            revert PythErrors.InvalidTwapUpdateDataSet();
+        }
+        if (twapPriceInfoFirst.publishTime > twapPriceInfoSecond.publishTime) {
+            revert PythErrors.InvalidTwapUpdateDataSet();
+        }
     }
 
     function parsePriceFeedUpdatesUnique(

+ 62 - 14
target_chains/ethereum/contracts/contracts/pyth/PythAccumulator.sol

@@ -263,18 +263,66 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
                     encodedMessage,
                     1
                 );
-            } else if (messageType == MessageType.TwapPriceFeed) {
+            } else revert PythErrors.InvalidUpdateData();
+
+            return (endOffset, priceInfo, priceId, prevPublishTime);
+        }
+    }
+
+    function extractTwapPriceInfoFromMerkleProof(
+        bytes20 digest,
+        bytes calldata encoded,
+        uint offset
+    )
+        internal
+        pure
+        returns (
+            uint endOffset,
+            PythInternalStructs.TwapPriceInfo memory twapPriceInfo,
+            bytes32 priceId
+        )
+        // TODO: Add the logic to extract the twap price info from the merkle proof
+    {
+                unchecked {
+            bytes calldata encodedMessage;
+            uint16 messageSize = UnsafeCalldataBytesLib.toUint16(
+                encoded,
+                offset
+            );
+            offset += 2;
+
+            encodedMessage = UnsafeCalldataBytesLib.slice(
+                encoded,
+                offset,
+                messageSize
+            );
+            offset += messageSize;
+
+            bool valid;
+            (valid, endOffset) = MerkleTree.isProofValid(
+                encoded,
+                offset,
+                digest,
+                encodedMessage
+            );
+            if (!valid) {
+                revert PythErrors.InvalidUpdateData();
+            }
+
+            MessageType messageType = MessageType(
+                UnsafeCalldataBytesLib.toUint8(encodedMessage, 0)
+            );
+            if (messageType == MessageType.TwapPriceFeed) {
                 (twapPriceInfo, priceId) = parseTwapPriceFeedMessage(
                     encodedMessage,
                     1
                 );
-            } else {
-                revert PythErrors.InvalidUpdateData();
-            }
+            } else revert PythErrors.InvalidUpdateData();
 
-            return (endOffset, priceInfo, priceId, prevPublishTime);
+            return (endOffset, twapPriceInfo, priceId);
         }
     }
+        
 
     function parsePriceFeedMessage(
         bytes calldata encodedPriceFeed,
@@ -354,52 +402,52 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
     {
         unchecked {
             priceId = UnsafeCalldataBytesLib.toBytes32(
-                encodedPriceFeed,
+                encodedTwapPriceFeed,
                 offset
             );
             offset += 32;
 
             twapPriceInfo.cumulativePrice = int128(
-                UnsafeCalldataBytesLib.toUint64(encodedPriceFeed, offset)
+                UnsafeCalldataBytesLib.toUint128(encodedTwapPriceFeed, offset)
             );
             offset += 16;
 
             twapPriceInfo.cumulativeConf = UnsafeCalldataBytesLib.toUint128(
-                encodedPriceFeed,
+                encodedTwapPriceFeed,
                 offset
             );
             offset += 16;
 
             twapPriceInfo.numDownSlots = UnsafeCalldataBytesLib.toUint64(
-                encodedPriceFeed,
+                encodedTwapPriceFeed,
                 offset
             );
             offset += 8;
 
             twapPriceInfo.publishSlot = UnsafeCalldataBytesLib.toUint64(
-                encodedPriceFeed,
+                encodedTwapPriceFeed,
                 offset
             );
             offset += 8;
 
             twapPriceInfo.publishTime = UnsafeCalldataBytesLib.toUint64(
-                encodedPriceFeed,
+                encodedTwapPriceFeed,
                 offset
             );
             offset += 8;
 
             twapPriceInfo.prevPublishTime = UnsafeCalldataBytesLib.toUint64(
-                encodedPriceFeed,
+                encodedTwapPriceFeed,
                 offset
             );
             offset += 8;
 
             twapPriceInfo.expo = int32(
-                UnsafeCalldataBytesLib.toUint32(encodedPriceFeed, offset)
+                UnsafeCalldataBytesLib.toUint32(encodedTwapPriceFeed, offset)
             );
             offset += 4;
 
-            if (offset > encodedPriceFeed.length)
+            if (offset > encodedTwapPriceFeed.length)
                 revert PythErrors.InvalidUpdateData();
         }
     }

+ 2 - 2
target_chains/ethereum/contracts/contracts/pyth/PythInternalStructs.sol

@@ -34,8 +34,8 @@ contract PythInternalStructs {
         // slot 2
         uint64 numDownSlots;
         uint64 publishSlot;
-        int64 publishTime;
-        int64 prevPublishTime;
+        uint64 publishTime;
+        uint64 prevPublishTime;
         // slot 3
         
         int32 expo;

+ 4 - 0
target_chains/ethereum/sdk/solidity/PythErrors.sol

@@ -45,4 +45,8 @@ library PythErrors {
     // The wormhole address to set in SetWormholeAddress governance is invalid.
     // Signature: 0x13d3ed82
     error InvalidWormholeAddressToSet();
+    // The twap update data is invalid.
+    error InvalidTwapUpdateData();
+    // The twap update data set is invalid.
+    error InvalidTwapUpdateDataSet();
 }

+ 2 - 2
target_chains/ethereum/sdk/solidity/PythStructs.sol

@@ -39,8 +39,8 @@ contract PythStructs {
         // End time of the TWAP
         uint endTime;
         // TWAP price
-        Price cumulativePrice;
+        Price twap;
         // Down slot ratio
         uint32 downSlotRatio;
     }
-1}
+}