[eth] Wrap parsing method with unchecked (#378)
* Move parsing method to unchecked There is no overflow possible there * Update another for loop to be unchecked
This commit is contained in:
parent
8db2af6ee5
commit
b0a2b13e22
|
@ -37,8 +37,10 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
|||
uint requiredFee = getUpdateFee(updateData);
|
||||
require(msg.value >= requiredFee, "insufficient paid fee amount");
|
||||
|
||||
for(uint i = 0; i < updateData.length; i++) {
|
||||
for(uint i = 0; i < updateData.length; ) {
|
||||
updatePriceBatchFromVm(updateData[i]);
|
||||
|
||||
unchecked { i++; }
|
||||
}
|
||||
|
||||
emit UpdatePriceFeeds(msg.sender, updateData.length, requiredFee);
|
||||
|
@ -58,158 +60,168 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
|||
}
|
||||
|
||||
function parseAndProcessBatchPriceAttestation(IWormhole.VM memory vm) internal {
|
||||
bytes memory encoded = vm.payload;
|
||||
uint index = 0;
|
||||
|
||||
// Check header
|
||||
{
|
||||
uint32 magic = encoded.toUint32(index);
|
||||
index += 4;
|
||||
require(magic == 0x50325748, "invalid magic value");
|
||||
|
||||
uint16 versionMajor = encoded.toUint16(index);
|
||||
index += 2;
|
||||
require(versionMajor == 3, "invalid version major, expected 3");
|
||||
|
||||
uint16 versionMinor = encoded.toUint16(index);
|
||||
index += 2;
|
||||
require(versionMinor >= 0, "invalid version minor, expected 0 or more");
|
||||
|
||||
uint16 hdrSize = encoded.toUint16(index);
|
||||
index += 2;
|
||||
|
||||
// NOTE(2022-04-19): Currently, only payloadId comes after
|
||||
// hdrSize. Future extra header fields must be read using a
|
||||
// separate offset to respect hdrSize, i.e.:
|
||||
//
|
||||
// uint hdrIndex = 0;
|
||||
// bpa.header.payloadId = encoded.toUint8(index + hdrIndex);
|
||||
// hdrIndex += 1;
|
||||
//
|
||||
// bpa.header.someNewField = encoded.toUint32(index + hdrIndex);
|
||||
// hdrIndex += 4;
|
||||
//
|
||||
// // Skip remaining unknown header bytes
|
||||
// index += bpa.header.hdrSize;
|
||||
|
||||
uint8 payloadId = encoded.toUint8(index);
|
||||
|
||||
// Skip remaining unknown header bytes
|
||||
index += hdrSize;
|
||||
|
||||
// Payload ID of 2 required for batch headerBa
|
||||
require(payloadId == 2, "invalid payload ID, expected 2 for BatchPriceAttestation");
|
||||
}
|
||||
|
||||
// Parse the number of attestations
|
||||
uint16 nAttestations = encoded.toUint16(index);
|
||||
index += 2;
|
||||
|
||||
// Parse the attestation size
|
||||
uint16 attestationSize = encoded.toUint16(index);
|
||||
index += 2;
|
||||
require(encoded.length == (index + (attestationSize * nAttestations)), "invalid BatchPriceAttestation size");
|
||||
|
||||
PythInternalStructs.PriceInfo memory info;
|
||||
bytes32 priceId;
|
||||
uint freshPrices = 0;
|
||||
|
||||
// Deserialize each attestation
|
||||
for (uint j=0; j < nAttestations; j++) {
|
||||
// NOTE: We don't advance the global index immediately.
|
||||
// attestationIndex is an attestation-local offset used
|
||||
// for readability and easier debugging.
|
||||
uint attestationIndex = 0;
|
||||
|
||||
// Unused bytes32 product id
|
||||
attestationIndex += 32;
|
||||
|
||||
priceId = encoded.toBytes32(index + attestationIndex);
|
||||
attestationIndex += 32;
|
||||
|
||||
info.price.price = int64(encoded.toUint64(index + attestationIndex));
|
||||
attestationIndex += 8;
|
||||
|
||||
info.price.conf = encoded.toUint64(index + attestationIndex);
|
||||
attestationIndex += 8;
|
||||
|
||||
info.price.expo = int32(encoded.toUint32(index + attestationIndex));
|
||||
info.emaPrice.expo = info.price.expo;
|
||||
attestationIndex += 4;
|
||||
|
||||
info.emaPrice.price = int64(encoded.toUint64(index + attestationIndex));
|
||||
attestationIndex += 8;
|
||||
|
||||
info.emaPrice.conf = encoded.toUint64(index + attestationIndex);
|
||||
attestationIndex += 8;
|
||||
// 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 = 0;
|
||||
|
||||
// Check header
|
||||
{
|
||||
// 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 = encoded.toUint8(index + attestationIndex);
|
||||
attestationIndex += 1;
|
||||
uint32 magic = encoded.toUint32(index);
|
||||
index += 4;
|
||||
require(magic == 0x50325748, "invalid magic value");
|
||||
|
||||
// Unused uint32 numPublishers
|
||||
attestationIndex += 4;
|
||||
uint16 versionMajor = encoded.toUint16(index);
|
||||
index += 2;
|
||||
require(versionMajor == 3, "invalid version major, expected 3");
|
||||
|
||||
// Unused uint32 numPublishers
|
||||
attestationIndex += 4;
|
||||
uint16 versionMinor = encoded.toUint16(index);
|
||||
index += 2;
|
||||
require(versionMinor >= 0, "invalid version minor, expected 0 or more");
|
||||
|
||||
// Unused uint64 attestationTime
|
||||
uint16 hdrSize = encoded.toUint16(index);
|
||||
index += 2;
|
||||
|
||||
// NOTE(2022-04-19): Currently, only payloadId comes after
|
||||
// hdrSize. Future extra header fields must be read using a
|
||||
// separate offset to respect hdrSize, i.e.:
|
||||
//
|
||||
// uint hdrIndex = 0;
|
||||
// bpa.header.payloadId = encoded.toUint8(index + hdrIndex);
|
||||
// hdrIndex += 1;
|
||||
//
|
||||
// bpa.header.someNewField = encoded.toUint32(index + hdrIndex);
|
||||
// hdrIndex += 4;
|
||||
//
|
||||
// // Skip remaining unknown header bytes
|
||||
// index += bpa.header.hdrSize;
|
||||
|
||||
uint8 payloadId = encoded.toUint8(index);
|
||||
|
||||
// Skip remaining unknown header bytes
|
||||
index += hdrSize;
|
||||
|
||||
// Payload ID of 2 required for batch headerBa
|
||||
require(payloadId == 2, "invalid payload ID, expected 2 for BatchPriceAttestation");
|
||||
}
|
||||
|
||||
// Parse the number of attestations
|
||||
uint16 nAttestations = encoded.toUint16(index);
|
||||
index += 2;
|
||||
|
||||
// Parse the attestation size
|
||||
uint16 attestationSize = encoded.toUint16(index);
|
||||
index += 2;
|
||||
|
||||
// Given the message is valid the arithmetic below should not overflow, and
|
||||
// even if it overflows then the require would fail.
|
||||
require(encoded.length == (index + (attestationSize * nAttestations)), "invalid BatchPriceAttestation size");
|
||||
|
||||
PythInternalStructs.PriceInfo memory info;
|
||||
bytes32 priceId;
|
||||
uint freshPrices = 0;
|
||||
|
||||
// Deserialize each attestation
|
||||
for (uint j=0; j < nAttestations; j++) {
|
||||
// NOTE: We don't advance the global index immediately.
|
||||
// attestationIndex is an attestation-local offset used
|
||||
// for readability and easier debugging.
|
||||
uint attestationIndex = 0;
|
||||
|
||||
// Unused bytes32 product id
|
||||
attestationIndex += 32;
|
||||
|
||||
priceId = encoded.toBytes32(index + attestationIndex);
|
||||
attestationIndex += 32;
|
||||
|
||||
info.price.price = int64(encoded.toUint64(index + attestationIndex));
|
||||
attestationIndex += 8;
|
||||
|
||||
info.price.publishTime = encoded.toUint64(index + attestationIndex);
|
||||
info.emaPrice.publishTime = info.price.publishTime;
|
||||
info.price.conf = encoded.toUint64(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.
|
||||
info.price.expo = int32(encoded.toUint32(index + attestationIndex));
|
||||
info.emaPrice.expo = info.price.expo;
|
||||
attestationIndex += 4;
|
||||
|
||||
info.emaPrice.price = int64(encoded.toUint64(index + attestationIndex));
|
||||
attestationIndex += 8;
|
||||
|
||||
info.emaPrice.conf = encoded.toUint64(index + attestationIndex);
|
||||
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 = encoded.toUint8(index + attestationIndex);
|
||||
attestationIndex += 1;
|
||||
|
||||
// Unused uint32 numPublishers
|
||||
attestationIndex += 4;
|
||||
|
||||
// Unused uint32 numPublishers
|
||||
attestationIndex += 4;
|
||||
|
||||
// Unused uint64 attestationTime
|
||||
attestationIndex += 8;
|
||||
|
||||
// Previous publish time
|
||||
info.price.publishTime = encoded.toUint64(index + attestationIndex);
|
||||
attestationIndex += 8;
|
||||
|
||||
// Previous price
|
||||
info.price.price = int64(encoded.toUint64(index + attestationIndex));
|
||||
attestationIndex += 8;
|
||||
|
||||
// Previous confidence
|
||||
info.price.conf = encoded.toUint64(index + attestationIndex);
|
||||
attestationIndex += 8;
|
||||
|
||||
// The EMA is last updated when the aggregate had trading status,
|
||||
// so, we use previous publish time here too.
|
||||
info.emaPrice.publishTime = info.price.publishTime;
|
||||
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.price.publishTime = encoded.toUint64(index + attestationIndex);
|
||||
attestationIndex += 8;
|
||||
|
||||
// Previous price
|
||||
info.price.price = int64(encoded.toUint64(index + attestationIndex));
|
||||
attestationIndex += 8;
|
||||
|
||||
// Previous confidence
|
||||
info.price.conf = encoded.toUint64(index + attestationIndex);
|
||||
attestationIndex += 8;
|
||||
|
||||
// The EMA is last updated when the aggregate had trading status,
|
||||
// so, we use previous publish time here too.
|
||||
info.emaPrice.publishTime = info.price.publishTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
require(attestationIndex <= attestationSize, "INTERNAL: Consumed more than `attestationSize` bytes");
|
||||
|
||||
// Respect specified attestation size for forward-compat
|
||||
index += attestationSize;
|
||||
|
||||
// Store the attestation
|
||||
uint64 latestPublishTime = latestPriceInfoPublishTime(priceId);
|
||||
|
||||
bool fresh = false;
|
||||
if(info.price.publishTime > latestPublishTime) {
|
||||
freshPrices += 1;
|
||||
fresh = true;
|
||||
setLatestPriceInfo(priceId, info);
|
||||
}
|
||||
|
||||
emit PriceFeedUpdate(priceId, fresh, vm.emitterChainId, vm.sequence, latestPublishTime,
|
||||
info.price.publishTime, info.price.price, info.price.conf);
|
||||
}
|
||||
|
||||
require(attestationIndex <= attestationSize, "INTERNAL: Consumed more than `attestationSize` bytes");
|
||||
|
||||
// Respect specified attestation size for forward-compat
|
||||
index += attestationSize;
|
||||
|
||||
// Store the attestation
|
||||
uint64 latestPublishTime = latestPriceInfoPublishTime(priceId);
|
||||
|
||||
bool fresh = false;
|
||||
if(info.price.publishTime > latestPublishTime) {
|
||||
freshPrices += 1;
|
||||
fresh = true;
|
||||
setLatestPriceInfo(priceId, info);
|
||||
}
|
||||
|
||||
emit PriceFeedUpdate(priceId, fresh, vm.emitterChainId, vm.sequence, latestPublishTime,
|
||||
info.price.publishTime, info.price.price, info.price.conf);
|
||||
emit BatchPriceFeedUpdate(vm.emitterChainId, vm.sequence, nAttestations, freshPrices);
|
||||
}
|
||||
|
||||
|
||||
emit BatchPriceFeedUpdate(vm.emitterChainId, vm.sequence, nAttestations, freshPrices);
|
||||
}
|
||||
|
||||
function queryPriceFeed(bytes32 id) public view override returns (PythStructs.PriceFeed memory priceFeed){
|
||||
|
|
Loading…
Reference in New Issue