Skip to content

Commit

Permalink
add pyth verifier
Browse files Browse the repository at this point in the history
  • Loading branch information
zkbenny committed Jan 9, 2024
1 parent 974141e commit 4f6c36a
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 0 deletions.
22 changes: 22 additions & 0 deletions contracts/oracle/IPyth.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "./IWormhole.sol";

/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth {
function wormhole() external view returns (IWormhole);

function singleUpdateFeeInWei() external view returns (uint);

/// @notice Update price feeds with given update messages.
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
/// Prices will be updated if they are more recent than the current stored prices.
/// The call will succeed even if the update is not the most recent.
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
function updatePriceFeeds(bytes[] calldata updateData) external payable;
}
142 changes: 142 additions & 0 deletions contracts/oracle/IWormhole.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// contracts/Messages.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

interface IWormhole {
struct GuardianSet {
address[] keys;
uint32 expirationTime;
}

struct Signature {
bytes32 r;
bytes32 s;
uint8 v;
uint8 guardianIndex;
}

struct VM {
uint8 version;
uint32 timestamp;
uint32 nonce;
uint16 emitterChainId;
bytes32 emitterAddress;
uint64 sequence;
uint8 consistencyLevel;
bytes payload;

uint32 guardianSetIndex;
Signature[] signatures;

bytes32 hash;
}

struct ContractUpgrade {
bytes32 module;
uint8 action;
uint16 chain;

address newContract;
}

struct GuardianSetUpgrade {
bytes32 module;
uint8 action;
uint16 chain;

GuardianSet newGuardianSet;
uint32 newGuardianSetIndex;
}

struct SetMessageFee {
bytes32 module;
uint8 action;
uint16 chain;

uint256 messageFee;
}

struct TransferFees {
bytes32 module;
uint8 action;
uint16 chain;

uint256 amount;
bytes32 recipient;
}

struct RecoverChainId {
bytes32 module;
uint8 action;

uint256 evmChainId;
uint16 newChainId;
}

event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
event ContractUpgraded(address indexed oldContract, address indexed newContract);
event GuardianSetAdded(uint32 indexed index);

function publishMessage(
uint32 nonce,
bytes memory payload,
uint8 consistencyLevel
) external payable returns (uint64 sequence);

function initialize() external;

function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason);

function verifyVM(VM memory vm) external view returns (bool valid, string memory reason);

function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason);

function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);

function quorum(uint numGuardians) external pure returns (uint numSignaturesRequiredForQuorum);

function getGuardianSet(uint32 index) external view returns (GuardianSet memory);

function getCurrentGuardianSetIndex() external view returns (uint32);

function getGuardianSetExpiry() external view returns (uint32);

function governanceActionIsConsumed(bytes32 hash) external view returns (bool);

function isInitialized(address impl) external view returns (bool);

function chainId() external view returns (uint16);

function isFork() external view returns (bool);

function governanceChainId() external view returns (uint16);

function governanceContract() external view returns (bytes32);

function messageFee() external view returns (uint256);

function evmChainId() external view returns (uint256);

function nextSequence(address emitter) external view returns (uint64);

function parseContractUpgrade(bytes memory encodedUpgrade) external pure returns (ContractUpgrade memory cu);

function parseGuardianSetUpgrade(bytes memory encodedUpgrade) external pure returns (GuardianSetUpgrade memory gsu);

function parseSetMessageFee(bytes memory encodedSetMessageFee) external pure returns (SetMessageFee memory smf);

function parseTransferFees(bytes memory encodedTransferFees) external pure returns (TransferFees memory tf);

function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci);

function submitContractUpgrade(bytes memory _vm) external;

function submitSetMessageFee(bytes memory _vm) external;

function submitNewGuardianSet(bytes memory _vm) external;

function submitTransferFees(bytes memory _vm) external;

function submitRecoverChainId(bytes memory _vm) external;
}
36 changes: 36 additions & 0 deletions contracts/oracle/PythVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

pragma solidity ^0.8.0;

import "./IPyth.sol";

/// @title Verify oracle committed data for pyth
/// @author zk.link
contract PythVerifier {

/// @notice The pyth contract
IPyth public immutable pyth;

constructor(IPyth _pyth) {
pyth = _pyth;
}

function verify(uint32 guardianSetIndex, bytes32 guardianSetAddressHash, uint256 totalNumUpdates) external {
// verify guardian set
IWormhole wormhole = pyth.wormhole();
IWormhole.GuardianSet memory guardianSet = wormhole.getGuardianSet(guardianSetIndex);
require(guardianSet.keys.length > 0, "Invalid guardian set index");
require(guardianSetIndex == wormhole.getCurrentGuardianSetIndex() || guardianSet.expirationTime >= block.timestamp, "Guardian set has expired");

// check the address set used to verify signature offchain is valid
bytes memory addressHashContent;
for (uint256 i = 0; i < guardianSet.keys.length; ++i) {
addressHashContent = abi.encodePacked(addressHashContent, guardianSet.keys[i]);
}
require(keccak256(addressHashContent) == guardianSetAddressHash, "Invalid guardian set address hash");

// calculate fee that need to pay for pyth
uint256 requiredFee = pyth.singleUpdateFeeInWei() * totalNumUpdates;
pyth.updatePriceFeeds{value: requiredFee}(new bytes[](0));
}
}

0 comments on commit 4f6c36a

Please sign in to comment.