Jelajahi Sumber

refactor(target_chains/ethereum): remove price attestations from tests

Pavel Strakhov 1 tahun lalu
induk
melakukan
7352256c63

+ 73 - 126
target_chains/ethereum/contracts/forge-test/VerificationExperiments.t.sol

@@ -179,7 +179,7 @@ contract VerificationExperiments is Test, WormholeTestUtils, PythTestUtils {
 
         thresholdUpdate = generateThresholdUpdate(priceIds[0], freshPrices[0]);
 
-        nativeUpdate = generateAttestationPayload(priceIds[0], freshPrices[0]);
+        nativeUpdate = generateMessagePayload(priceIds[0], freshPrices[0]);
     }
 
     // Get the payload for a wormhole batch price update
@@ -201,20 +201,18 @@ contract VerificationExperiments is Test, WormholeTestUtils, PythTestUtils {
     }
 
     // Helper function to serialize a single price update to bytes.
-    // Returns a serialized PriceAttestation.
-    function generateAttestationPayload(
+    // Returns a serialized PriceFeedMessage.
+    function generateMessagePayload(
         bytes32 priceId,
         PythStructs.Price memory price
     ) internal returns (bytes memory data) {
-        bytes32[] memory attestationPriceIds = new bytes32[](1);
-        attestationPriceIds[0] = priceId;
+        bytes32[] memory messagePriceIds = new bytes32[](1);
+        messagePriceIds[0] = priceId;
         PythStructs.Price[] memory prices = new PythStructs.Price[](1);
         prices[0] = price;
-        PriceAttestation[] memory attestation = pricesToPriceAttestations(
-            attestationPriceIds,
-            prices
-        );
-        data = generatePriceFeedUpdatePayload(attestation);
+        data = encodePriceFeedMessages(
+            pricesToPriceFeedMessages(messagePriceIds, prices)
+        )[0];
 
         return data;
     }
@@ -230,7 +228,7 @@ contract VerificationExperiments is Test, WormholeTestUtils, PythTestUtils {
         internal
         returns (bytes32 root, bytes memory data, bytes32[] memory proof)
     {
-        data = generateAttestationPayload(priceId, price);
+        data = generateMessagePayload(priceId, price);
 
         bytes32 curNodeHash = keccak256(data);
         proof = new bytes32[](depth);
@@ -287,7 +285,7 @@ contract VerificationExperiments is Test, WormholeTestUtils, PythTestUtils {
             bytes32[] memory proof
         ) = generateMerkleProof(priceId, price, depth);
 
-        data = generateAttestationPayload(priceId, price);
+        data = generateMessagePayload(priceId, price);
         bytes32 hash = keccak256(data);
 
         (uint8 v, bytes32 r, bytes32 s) = vm.sign(THRESHOLD_KEY, root);
@@ -296,12 +294,12 @@ contract VerificationExperiments is Test, WormholeTestUtils, PythTestUtils {
         return ThresholdMerkleUpdate(signature, root, data, proof);
     }
 
-    // Generate a threshold-signed price attestation.
+    // Generate a threshold-signed price feed message.
     function generateThresholdUpdate(
         bytes32 priceId,
         PythStructs.Price memory price
     ) internal returns (ThresholdUpdate memory update) {
-        bytes memory data = generateAttestationPayload(priceId, price);
+        bytes memory data = generateMessagePayload(priceId, price);
         bytes32 hash = keccak256(data);
 
         (uint8 v, bytes32 r, bytes32 s) = vm.sign(THRESHOLD_KEY, hash);
@@ -409,13 +407,13 @@ contract PythExperimental is Pyth {
     }
 
     // Update a single price feed via a wormhole-attested merkle proof.
-    // data is expected to be a serialized PriceAttestation
+    // data is expected to be a serialized PriceFeedMessage
     function updatePriceFeedsWhMerkle(
         bytes calldata rootVaa,
         bytes memory data,
         bytes32[] memory proof
     ) public payable {
-        IWormhole.VM memory vm = parseAndVerifyBatchAttestationVM(rootVaa);
+        IWormhole.VM memory vm = parseAndVerifyBatchMessageVM(rootVaa);
         assert(vm.payload.length == 32);
 
         bytes32 expectedRoot = UnsafeBytesLib.toBytes32(vm.payload, 0);
@@ -424,12 +422,14 @@ contract PythExperimental is Pyth {
 
         (
             PythInternalStructs.PriceInfo memory info,
-            bytes32 priceId
-        ) = parseSingleAttestationFromBatch(data, 0, data.length);
+            bytes32 priceId,
+
+        ) = parsePriceFeedMessageFromMemory(data, 0);
+
         updateLatestPriceIfNecessary(priceId, info);
     }
 
-    function parseAndVerifyBatchAttestationVM(
+    function parseAndVerifyBatchMessageVM(
         bytes calldata encodedVm
     ) internal view returns (IWormhole.VM memory vm) {
         {
@@ -448,7 +448,7 @@ contract PythExperimental is Pyth {
     }
 
     // Update a single price feed via a threshold-signed merkle proof.
-    // data is expected to be a serialized PriceAttestation
+    // data is expected to be a serialized PriceFeedMessage
     function updatePriceFeedsThresholdMerkle(
         bytes memory rootSignature,
         bytes32 rootHash,
@@ -463,13 +463,14 @@ contract PythExperimental is Pyth {
 
         (
             PythInternalStructs.PriceInfo memory info,
-            bytes32 priceId
-        ) = parseSingleAttestationFromBatch(data, 0, data.length);
+            bytes32 priceId,
+
+        ) = parsePriceFeedMessageFromMemory(data, 0);
         updateLatestPriceIfNecessary(priceId, info);
     }
 
     // Update a single price feed via a threshold-signed price update.
-    // data is expected to be a serialized PriceAttestation.
+    // data is expected to be a serialized PriceFeedMessage.
     function updatePriceFeedsThreshold(
         bytes memory signature,
         bytes memory data
@@ -480,13 +481,14 @@ contract PythExperimental is Pyth {
 
         (
             PythInternalStructs.PriceInfo memory info,
-            bytes32 priceId
-        ) = parseSingleAttestationFromBatch(data, 0, data.length);
+            bytes32 priceId,
+
+        ) = parsePriceFeedMessageFromMemory(data, 0);
         updateLatestPriceIfNecessary(priceId, info);
     }
 
     // Update a single price feed via a "native" price update (i.e., using the default ethereum tx signature for authentication).
-    // data is expected to be a serialized PriceAttestation.
+    // data is expected to be a serialized PriceFeedMessage.
     // This function represents the lower bound on how much gas we can use.
     function updatePriceFeedsNative(bytes memory data) public payable {
         // TODO: this function should have a check on the sender.
@@ -494,122 +496,67 @@ contract PythExperimental is Pyth {
 
         (
             PythInternalStructs.PriceInfo memory info,
-            bytes32 priceId
-        ) = parseSingleAttestationFromBatch(data, 0, data.length);
+            bytes32 priceId,
+
+        ) = parsePriceFeedMessageFromMemory(data, 0);
         updateLatestPriceIfNecessary(priceId, info);
     }
 
-    function parseSingleAttestationFromBatch(
-        bytes memory encoded,
-        uint index,
-        uint attestationSize
+    function parsePriceFeedMessageFromMemory(
+        bytes memory encodedPriceFeed,
+        uint offset
     )
         internal
         pure
-        returns (PythInternalStructs.PriceInfo memory info, bytes32 priceId)
+        returns (
+            PythInternalStructs.PriceInfo memory priceInfo,
+            bytes32 priceId,
+            uint64 prevPublishTime
+        )
     {
         unchecked {
-            // NOTE: We don't advance the global index immediately.
-            // attestationIndex is an attestation-local offset used
-            // for readability and easier debugging.
-            uint attestationIndex = 0;
+            priceId = UnsafeBytesLib.toBytes32(encodedPriceFeed, offset);
+            offset += 32;
 
-            // Unused bytes32 product id
-            attestationIndex += 32;
-
-            priceId = UnsafeBytesLib.toBytes32(
-                encoded,
-                index + attestationIndex
+            priceInfo.price = int64(
+                UnsafeBytesLib.toUint64(encodedPriceFeed, offset)
             );
-            attestationIndex += 32;
+            offset += 8;
 
-            info.price = int64(
-                UnsafeBytesLib.toUint64(encoded, index + attestationIndex)
-            );
-            attestationIndex += 8;
+            priceInfo.conf = UnsafeBytesLib.toUint64(encodedPriceFeed, offset);
+            offset += 8;
 
-            info.conf = UnsafeBytesLib.toUint64(
-                encoded,
-                index + attestationIndex
+            priceInfo.expo = int32(
+                UnsafeBytesLib.toUint32(encodedPriceFeed, offset)
             );
-            attestationIndex += 8;
-
-            info.expo = int32(
-                UnsafeBytesLib.toUint32(encoded, index + attestationIndex)
+            offset += 4;
+
+            // Publish time is i64 in some environments due to the standard in that
+            // environment. This would not cause any problem because since the signed
+            // integer is represented in two's complement, the value would be the same
+            // in both cases (for a million year at least)
+            priceInfo.publishTime = UnsafeBytesLib.toUint64(
+                encodedPriceFeed,
+                offset
             );
-            attestationIndex += 4;
+            offset += 8;
 
-            info.emaPrice = int64(
-                UnsafeBytesLib.toUint64(encoded, index + attestationIndex)
+            // We do not store this field because it is not used on the latest feed queries.
+            prevPublishTime = UnsafeBytesLib.toUint64(encodedPriceFeed, offset);
+            offset += 8;
+
+            priceInfo.emaPrice = int64(
+                UnsafeBytesLib.toUint64(encodedPriceFeed, offset)
             );
-            attestationIndex += 8;
+            offset += 8;
 
-            info.emaConf = UnsafeBytesLib.toUint64(
-                encoded,
-                index + attestationIndex
+            priceInfo.emaConf = UnsafeBytesLib.toUint64(
+                encodedPriceFeed,
+                offset
             );
-            attestationIndex += 8;
-
-            {
-                // Status is an enum (encoded as uint8) with the following values:
-                // 0 = UNKNOWN: The price feed is not currently updating for an unknown reason.
-                // 1 = TRADING: The price feed is updating as expected.
-                // 2 = HALTED: The price feed is not currently updating because trading in the product has been halted.
-                // 3 = AUCTION: The price feed is not currently updating because an auction is setting the price.
-                uint8 status = UnsafeBytesLib.toUint8(
-                    encoded,
-                    index + attestationIndex
-                );
-                attestationIndex += 1;
-
-                // Unused uint32 numPublishers
-                attestationIndex += 4;
-
-                // Unused uint32 numPublishers
-                attestationIndex += 4;
-
-                // Unused uint64 attestationTime
-                attestationIndex += 8;
-
-                info.publishTime = UnsafeBytesLib.toUint64(
-                    encoded,
-                    index + attestationIndex
-                );
-                attestationIndex += 8;
-
-                if (status == 1) {
-                    // status == TRADING
-                    attestationIndex += 24;
-                } else {
-                    // If status is not trading then the latest available price is
-                    // the previous price info that are passed here.
-
-                    // Previous publish time
-                    info.publishTime = UnsafeBytesLib.toUint64(
-                        encoded,
-                        index + attestationIndex
-                    );
-                    attestationIndex += 8;
-
-                    // Previous price
-                    info.price = int64(
-                        UnsafeBytesLib.toUint64(
-                            encoded,
-                            index + attestationIndex
-                        )
-                    );
-                    attestationIndex += 8;
-
-                    // Previous confidence
-                    info.conf = UnsafeBytesLib.toUint64(
-                        encoded,
-                        index + attestationIndex
-                    );
-                    attestationIndex += 8;
-                }
-            }
+            offset += 8;
 
-            if (attestationIndex > attestationSize)
+            if (offset > encodedPriceFeed.length)
                 revert PythErrors.InvalidUpdateData();
         }
     }
@@ -665,7 +612,7 @@ struct WormholeMerkleUpdate {
     // The serialized bytes of a wormhole VAA
     // The payload of this VAA is a single 32-byte root hash for the merkle tree.
     bytes rootVaa;
-    // The serialized bytes of a PriceAttestation
+    // The serialized bytes of a PriceFeedMessage
     bytes data;
     // The chain of proof nodes.
     bytes32[] proof;
@@ -677,7 +624,7 @@ struct WormholeMerkleUpdate {
 struct ThresholdMerkleUpdate {
     bytes rootSignature;
     bytes32 rootHash;
-    // The serialized bytes of a PriceAttestation
+    // The serialized bytes of a PriceFeedMessage
     bytes data;
     // The chain of proof nodes.
     bytes32[] proof;
@@ -689,6 +636,6 @@ struct ThresholdMerkleUpdate {
 struct ThresholdUpdate {
     // Signature of the hash of the data.
     bytes signature;
-    // The serialized bytes of a PriceAttestation
+    // The serialized bytes of a PriceFeedMessage
     bytes data;
 }

+ 1 - 101
target_chains/ethereum/contracts/forge-test/utils/PythTestUtils.t.sol

@@ -59,30 +59,7 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
         return SINGLE_UPDATE_FEE_IN_WEI;
     }
 
-    /// Utilities to help generating price attestations and VAAs for them
-
-    enum PriceAttestationStatus {
-        Unknown,
-        Trading
-    }
-
-    struct PriceAttestation {
-        bytes32 productId;
-        bytes32 priceId;
-        int64 price;
-        uint64 conf;
-        int32 expo;
-        int64 emaPrice;
-        uint64 emaConf;
-        PriceAttestationStatus status;
-        uint32 numPublishers;
-        uint32 maxNumPublishers;
-        uint64 attestationTime;
-        uint64 publishTime;
-        uint64 prevPublishTime;
-        int64 prevPrice;
-        uint64 prevConf;
-    }
+    /// Utilities to help generating price feed messages and VAAs for them
 
     struct PriceFeedMessage {
         bytes32 priceId;
@@ -277,83 +254,6 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
         );
     }
 
-    // Generates byte-encoded payload for the given price attestations. You can use this to mock wormhole
-    // call using `vm.mockCall` and return a VM struct with this payload.
-    // You can use generatePriceFeedUpdate to generate a VAA for a price update.
-    function generatePriceFeedUpdatePayload(
-        PriceAttestation[] memory attestations
-    ) public pure returns (bytes memory payload) {
-        bytes memory encodedAttestations = new bytes(0);
-
-        for (uint i = 0; i < attestations.length; ++i) {
-            // encodePacked uses padding for arrays and we don't want it, so we manually concat them.
-            encodedAttestations = abi.encodePacked(
-                encodedAttestations,
-                attestations[i].productId,
-                attestations[i].priceId,
-                attestations[i].price,
-                attestations[i].conf,
-                attestations[i].expo,
-                attestations[i].emaPrice,
-                attestations[i].emaConf
-            );
-
-            // Breaking this in two encodePackes because of the limited EVM stack.
-            encodedAttestations = abi.encodePacked(
-                encodedAttestations,
-                uint8(attestations[i].status),
-                attestations[i].numPublishers,
-                attestations[i].maxNumPublishers,
-                attestations[i].attestationTime,
-                attestations[i].publishTime,
-                attestations[i].prevPublishTime,
-                attestations[i].prevPrice,
-                attestations[i].prevConf
-            );
-        }
-
-        payload = abi.encodePacked(
-            uint32(0x50325748), // Magic
-            uint16(3), // Major version
-            uint16(0), // Minor version
-            uint16(1), // Header size of 1 byte as it only contains payloadId
-            uint8(2), // Payload ID 2 means it's a batch price attestation
-            uint16(attestations.length), // Number of attestations
-            uint16(encodedAttestations.length / attestations.length), // Size of a single price attestation.
-            encodedAttestations
-        );
-    }
-
-    function pricesToPriceAttestations(
-        bytes32[] memory priceIds,
-        PythStructs.Price[] memory prices
-    ) public returns (PriceAttestation[] memory attestations) {
-        assertEq(priceIds.length, prices.length);
-        attestations = new PriceAttestation[](prices.length);
-
-        for (uint i = 0; i < prices.length; ++i) {
-            // Product ID, we use the same price Id. This field is not used.
-            attestations[i].productId = priceIds[i];
-            attestations[i].priceId = priceIds[i];
-            attestations[i].price = prices[i].price;
-            attestations[i].conf = prices[i].conf;
-            attestations[i].expo = prices[i].expo;
-            // Same price and conf is used for emaPrice and emaConf
-            attestations[i].emaPrice = prices[i].price;
-            attestations[i].emaConf = prices[i].conf;
-            attestations[i].status = PriceAttestationStatus.Trading;
-            attestations[i].numPublishers = 5; // This field is not used
-            attestations[i].maxNumPublishers = 10; // This field is not used
-            attestations[i].attestationTime = uint64(prices[i].publishTime); // This field is not used
-            attestations[i].publishTime = uint64(prices[i].publishTime);
-            // Fields below are not used when status is Trading. just setting them to
-            // the same value as the prices.
-            attestations[i].prevPublishTime = uint64(prices[i].publishTime);
-            attestations[i].prevPrice = prices[i].price;
-            attestations[i].prevConf = prices[i].conf;
-        }
-    }
-
     function pricesToPriceFeedMessages(
         bytes32[] memory priceIds,
         PythStructs.Price[] memory prices