refactor(target_chains/ethereum): remove legacy batch updates support from updatePriceFeeds

This commit is contained in:
Pavel Strakhov 2024-03-25 11:52:19 +00:00
parent 01f878cf5a
commit 8a70ca769b
4 changed files with 49 additions and 177 deletions

View File

@ -62,29 +62,14 @@ abstract contract Pyth is
PythSetters.setSingleUpdateFeeInWei(singleUpdateFeeInWei);
}
function updatePriceBatchFromVm(bytes calldata encodedVm) private {
parseAndProcessBatchPriceAttestation(
parseAndVerifyBatchAttestationVM(encodedVm)
);
}
function updatePriceFeeds(
bytes[] calldata updateData
) public payable override {
uint totalNumUpdates = 0;
for (uint i = 0; i < updateData.length; ) {
if (
updateData[i].length > 4 &&
UnsafeCalldataBytesLib.toUint32(updateData[i], 0) ==
ACCUMULATOR_MAGIC
) {
totalNumUpdates += updatePriceInfosFromAccumulatorUpdate(
updateData[i]
);
} else {
updatePriceBatchFromVm(updateData[i]);
totalNumUpdates += 1;
}
totalNumUpdates += updatePriceInfosFromAccumulatorUpdate(
updateData[i]
);
unchecked {
i++;
@ -138,43 +123,6 @@ abstract contract Pyth is
return isValidDataSource(vm.emitterChainId, vm.emitterAddress);
}
function parseAndProcessBatchPriceAttestation(
IWormhole.VM memory vm
) internal {
// Most of the math operations below are simple additions.
// In the places that there is more complex operation there is
// a comment explaining why it is safe. Also, byteslib
// operations have proper require.
unchecked {
bytes memory encoded = vm.payload;
(
uint index,
uint nAttestations,
uint attestationSize
) = parseBatchAttestationHeader(encoded);
// Deserialize each attestation
for (uint j = 0; j < nAttestations; j++) {
(
PythInternalStructs.PriceInfo memory info,
bytes32 priceId
) = parseSingleAttestationFromBatch(
encoded,
index,
attestationSize
);
// Respect specified attestation size for forward-compat
index += attestationSize;
// Store the attestation
updateLatestPriceIfNecessary(priceId, info);
}
emit BatchPriceFeedUpdate(vm.emitterChainId, vm.sequence);
}
}
function parseSingleAttestationFromBatch(
bytes memory encoded,
uint index,

View File

@ -32,21 +32,21 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
// Cached prices are populated in the setUp
PythStructs.Price[] cachedPrices;
bytes[] cachedPricesWhBatchUpdateData;
uint cachedPricesWhBatchUpdateFee;
uint64[] cachedPricesPublishTimes;
bytes[][] cachedPricesWhMerkleUpdateData; // i th element contains the update data for the first i prices
bytes[] allCachedPricesWhMerkleUpdateData; // the update data for all prices
uint[] cachedPricesWhMerkleUpdateFee; // i th element contains the update fee for the first i prices
uint allCachedPricesWhMerkleUpdateFee; // the update fee for all prices
// Fresh prices are different prices that can be used
// as a fresh price to update the prices
PythStructs.Price[] freshPrices;
bytes[] freshPricesWhBatchUpdateData;
uint freshPricesWhBatchUpdateFee;
uint64[] freshPricesPublishTimes;
bytes[][] freshPricesWhMerkleUpdateData; // i th element contains the update data for the first i prices
bytes[] allFreshPricesWhMerkleUpdateData; // the update data for all prices
uint[] freshPricesWhMerkleUpdateFee; // i th element contains the update fee for the first i prices
uint allFreshPricesWhMerkleUpdateFee; // the update fee for all prices
uint64 sequence;
uint randomSeed;
@ -104,19 +104,23 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
freshPricesWhMerkleUpdateData.push(updateData);
freshPricesWhMerkleUpdateFee.push(updateFee);
}
// Populate the contract with the initial prices
(
cachedPricesWhBatchUpdateData,
cachedPricesWhBatchUpdateFee
) = generateWhBatchUpdateDataAndFee(cachedPrices);
pyth.updatePriceFeeds{value: cachedPricesWhBatchUpdateFee}(
cachedPricesWhBatchUpdateData
);
allCachedPricesWhMerkleUpdateData = cachedPricesWhMerkleUpdateData[
NUM_PRICES - 1
];
allCachedPricesWhMerkleUpdateFee = cachedPricesWhMerkleUpdateFee[
NUM_PRICES - 1
];
allFreshPricesWhMerkleUpdateData = freshPricesWhMerkleUpdateData[
NUM_PRICES - 1
];
allFreshPricesWhMerkleUpdateFee = freshPricesWhMerkleUpdateFee[
NUM_PRICES - 1
];
(
freshPricesWhBatchUpdateData,
freshPricesWhBatchUpdateFee
) = generateWhBatchUpdateDataAndFee(freshPrices);
// Populate the contract with the initial prices
pyth.updatePriceFeeds{value: allCachedPricesWhMerkleUpdateFee}(
allCachedPricesWhMerkleUpdateData
);
}
function getRand() internal returns (uint val) {
@ -124,23 +128,6 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
val = uint(keccak256(abi.encode(randomSeed)));
}
function generateWhBatchUpdateDataAndFee(
PythStructs.Price[] memory prices
) internal returns (bytes[] memory updateData, uint updateFee) {
bytes memory vaa = generateWhBatchUpdate(
pricesToPriceAttestations(priceIds, prices),
sequence,
NUM_GUARDIAN_SIGNERS
);
++sequence;
updateData = new bytes[](1);
updateData[0] = vaa;
updateFee = pyth.getUpdateFee(updateData);
}
function generateWhMerkleUpdateDataAndFee(
PythStructs.Price[] memory prices
) internal returns (bytes[] memory updateData, uint updateFee) {
@ -155,18 +142,6 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
updateFee = pyth.getUpdateFee(updateData);
}
function testBenchmarkUpdatePriceFeedsWhBatchFresh() public {
pyth.updatePriceFeeds{value: freshPricesWhBatchUpdateFee}(
freshPricesWhBatchUpdateData
);
}
function testBenchmarkUpdatePriceFeedsWhBatchNotFresh() public {
pyth.updatePriceFeeds{value: cachedPricesWhBatchUpdateFee}(
cachedPricesWhBatchUpdateData
);
}
function testBenchmarkUpdatePriceFeedsWhMerkle1FeedFresh() public {
pyth.updatePriceFeeds{value: freshPricesWhMerkleUpdateFee[0]}(
freshPricesWhMerkleUpdateData[0]
@ -227,23 +202,23 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
);
}
function testBenchmarkUpdatePriceFeedsIfNecessaryWhBatchFresh() public {
function testBenchmarkUpdatePriceFeedsIfNecessaryWhMerkleFresh() public {
// Since the prices have advanced, the publishTimes are newer than one in
// the contract and hence, the call should succeed.
pyth.updatePriceFeedsIfNecessary{value: freshPricesWhBatchUpdateFee}(
freshPricesWhBatchUpdateData,
priceIds,
freshPricesPublishTimes
);
pyth.updatePriceFeedsIfNecessary{
value: allFreshPricesWhMerkleUpdateFee
}(allFreshPricesWhMerkleUpdateData, priceIds, freshPricesPublishTimes);
}
function testBenchmarkUpdatePriceFeedsIfNecessaryWhBatchNotFresh() public {
function testBenchmarkUpdatePriceFeedsIfNecessaryWhMerkleNotFresh() public {
// Since the price is not advanced, the publishTimes are the same as the
// ones in the contract.
vm.expectRevert(PythErrors.NoFreshUpdate.selector);
pyth.updatePriceFeedsIfNecessary{value: cachedPricesWhBatchUpdateFee}(
cachedPricesWhBatchUpdateData,
pyth.updatePriceFeedsIfNecessary{
value: allCachedPricesWhMerkleUpdateFee
}(
allCachedPricesWhMerkleUpdateData,
priceIds,
cachedPricesPublishTimes
);
@ -253,8 +228,8 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
bytes32[] memory ids = new bytes32[](1);
ids[0] = priceIds[0];
pyth.parsePriceFeedUpdates{value: freshPricesWhBatchUpdateFee}(
freshPricesWhBatchUpdateData,
pyth.parsePriceFeedUpdates{value: allFreshPricesWhMerkleUpdateFee}(
allFreshPricesWhMerkleUpdateData,
ids,
0,
50
@ -266,8 +241,8 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
ids[0] = priceIds[0];
ids[1] = priceIds[1];
pyth.parsePriceFeedUpdates{value: freshPricesWhBatchUpdateFee}(
freshPricesWhBatchUpdateData,
pyth.parsePriceFeedUpdates{value: allFreshPricesWhMerkleUpdateFee}(
allFreshPricesWhMerkleUpdateData,
ids,
0,
50
@ -403,8 +378,8 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
ids[0] = priceIds[0];
vm.expectRevert(PythErrors.PriceFeedNotFoundWithinRange.selector);
pyth.parsePriceFeedUpdates{value: freshPricesWhBatchUpdateFee}(
freshPricesWhBatchUpdateData,
pyth.parsePriceFeedUpdates{value: allFreshPricesWhMerkleUpdateFee}(
allFreshPricesWhMerkleUpdateData,
ids,
50,
100
@ -427,10 +402,6 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
pyth.getEmaPrice(priceIds[0]);
}
function testBenchmarkGetUpdateFeeWhBatch() public view {
pyth.getUpdateFee(freshPricesWhBatchUpdateData);
}
function testBenchmarkGetUpdateFeeWhMerkle1() public view {
pyth.getUpdateFee(freshPricesWhMerkleUpdateData[0]);
}
@ -450,8 +421,4 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
function testBenchmarkGetUpdateFeeWhMerkle5() public view {
pyth.getUpdateFee(freshPricesWhMerkleUpdateData[4]);
}
function testBenchmarkWormholeParseAndVerifyVMBatchAttestation() public {
wormhole.parseAndVerifyVM(freshPricesWhBatchUpdateData[0]);
}
}

View File

@ -35,6 +35,9 @@ contract VerificationExperiments is
// Private key for the threshold signature
uint256 THRESHOLD_KEY = 1234;
// We will have less than 512 price for a foreseeable future.
uint8 constant MERKLE_TREE_DEPTH = 9;
PythExperimental public pyth;
bytes32[] priceIds;
@ -106,7 +109,7 @@ contract VerificationExperiments is
setRandSeed(12345);
for (uint i = 0; i < NUM_PRICES; ++i) {
uint64 publishTime = uint64(getRandUint() % 10);
uint64 publishTime = uint64(getRandUint() % 10) + 1; // to make sure prevPublishTime is >= 0
cachedPrices.push(
PythStructs.Price(
@ -147,17 +150,17 @@ contract VerificationExperiments is
// Generate the update payloads for the various verification systems
whMerkleUpdateDepth0 = generateWhMerkleUpdate(
whMerkleUpdateDepth0 = generateSingleWhMerkleUpdate(
priceIds[0],
freshPrices[0],
0
);
whMerkleUpdateDepth1 = generateWhMerkleUpdate(
whMerkleUpdateDepth1 = generateSingleWhMerkleUpdate(
priceIds[0],
freshPrices[0],
1
);
whMerkleUpdateDepth8 = generateWhMerkleUpdate(
whMerkleUpdateDepth8 = generateSingleWhMerkleUpdate(
priceIds[0],
freshPrices[0],
8
@ -188,9 +191,9 @@ contract VerificationExperiments is
function generateWormholeUpdateDataAndFee(
PythStructs.Price[] memory prices
) internal returns (bytes[] memory updateData, uint updateFee) {
bytes memory vaa = generateWhBatchUpdate(
pricesToPriceAttestations(priceIds, prices),
sequence,
bytes memory vaa = generateWhMerkleUpdate(
pricesToPriceFeedMessages(priceIds, prices),
MERKLE_TREE_DEPTH,
NUM_GUARDIAN_SIGNERS
);
@ -252,7 +255,7 @@ contract VerificationExperiments is
}
// Generate a wormhole-attested merkle proof with the given depth.
function generateWhMerkleUpdate(
function generateSingleWhMerkleUpdate(
bytes32 priceId,
PythStructs.Price memory price,
uint depth

View File

@ -362,49 +362,3 @@ abstract contract PythTestUtils is Test, WormholeTestUtils {
}
}
}
contract PythTestUtilsTest is
Test,
WormholeTestUtils,
PythTestUtils,
IPythEvents
{
function testGenerateWhBatchUpdateWorks() public {
IPyth pyth = IPyth(
setUpPyth(
setUpWormholeReceiver(
1 // Number of guardians
)
)
);
bytes32[] memory priceIds = new bytes32[](1);
priceIds[
0
] = 0x0000000000000000000000000000000000000000000000000000000000000222;
PythStructs.Price[] memory prices = new PythStructs.Price[](1);
prices[0] = PythStructs.Price(
100, // Price
10, // Confidence
-5, // Exponent
1 // Publish time
);
bytes memory vaa = generateWhBatchUpdate(
pricesToPriceAttestations(priceIds, prices),
1, // Sequence
1 // No. Signers
);
bytes[] memory updateData = new bytes[](1);
updateData[0] = vaa;
uint updateFee = pyth.getUpdateFee(updateData);
vm.expectEmit(true, false, false, true);
emit PriceFeedUpdate(priceIds[0], 1, 100, 10);
pyth.updatePriceFeeds{value: updateFee}(updateData);
}
}