Răsfoiți Sursa

Add verifier to pyth evm contract

Danial Mehrjerdi 5 luni în urmă
părinte
comite
e6da773a36

+ 3 - 1
target_chains/ethereum/contracts/contracts/pyth/Pyth.sol

@@ -25,9 +25,11 @@ abstract contract Pyth is
         bytes32 governanceEmitterAddress,
         uint64 governanceInitialSequence,
         uint validTimePeriodSeconds,
-        uint singleUpdateFeeInWei
+        uint singleUpdateFeeInWei,
+        address verifier
     ) internal {
         setWormhole(wormhole);
+        setVerifier(verifier);
 
         if (
             dataSourceEmitterChainIds.length !=

+ 9 - 1
target_chains/ethereum/contracts/contracts/pyth/PythAccumulator.sol

@@ -37,7 +37,15 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth {
         {
             bool valid;
             (vm, valid, ) = wormhole().parseAndVerifyVM(encodedVm);
-            if (!valid) revert PythErrors.InvalidWormholeVaa();
+            if (!valid) {
+                if (address(verifier()) == address(0)) {
+                    revert PythErrors.InvalidUpdateData();
+                }
+                (vm, valid, ) = verifier().parseAndVerifyVM(encodedVm);
+                if (!valid) {
+                    revert PythErrors.InvalidUpdateData();
+                }
+            }
         }
 
         if (!isValidDataSource(vm.emitterChainId, vm.emitterAddress))

+ 4 - 0
target_chains/ethereum/contracts/contracts/pyth/PythGetters.sol

@@ -95,4 +95,8 @@ contract PythGetters is PythState {
     function transactionFeeInWei() public view returns (uint) {
         return _state.transactionFeeInWei;
     }
+
+    function verifier() public view returns (IWormhole) {
+        return IWormhole(_state.verifier);
+    }
 }

+ 76 - 8
target_chains/ethereum/contracts/contracts/pyth/PythGovernance.sol

@@ -40,15 +40,33 @@ abstract contract PythGovernance is
     );
     event TransactionFeeSet(uint oldFee, uint newFee);
     event FeeWithdrawn(address targetAddress, uint fee);
+    event VerifierAddressSet(
+        address oldVerifierAddress,
+        address newVerifierAddress
+    );
 
-    function verifyGovernanceVM(
+    // Check whether the encodedVM is signed by either the Wormhole or the Verifier.
+    function validateVm(
         bytes memory encodedVM
-    ) internal returns (IWormhole.VM memory parsedVM) {
+    ) internal view returns (IWormhole.VM memory parsedVM) {
         (IWormhole.VM memory vm, bool valid, ) = wormhole().parseAndVerifyVM(
             encodedVM
         );
+        if (valid) return vm;
 
-        if (!valid) revert PythErrors.InvalidWormholeVaa();
+        if (address(verifier()) != address(0)) {
+            (IWormhole.VM memory vmv, bool validv, ) = verifier()
+                .parseAndVerifyVM(encodedVM);
+            if (validv) return vmv;
+        }
+
+        revert PythErrors.InvalidWormholeVaa();
+    }
+
+    function verifyGovernanceVM(
+        bytes memory encodedVM
+    ) internal returns (IWormhole.VM memory parsedVM) {
+        IWormhole.VM memory vm = validateVm(encodedVM);
 
         if (!isValidGovernanceDataSource(vm.emitterChainId, vm.emitterAddress))
             revert PythErrors.InvalidGovernanceDataSource();
@@ -105,6 +123,13 @@ abstract contract PythGovernance is
             setTransactionFee(parseSetTransactionFeePayload(gi.payload));
         } else if (gi.action == GovernanceAction.WithdrawFee) {
             withdrawFee(parseWithdrawFeePayload(gi.payload));
+        } else if (gi.action == GovernanceAction.SetVerifierAddress) {
+            if (gi.targetChainId == 0)
+                revert PythErrors.InvalidGovernanceTarget();
+            setVerifierAddress(
+                parseSetVerifierAddressPayload(gi.payload),
+                encodedVM
+            );
         } else {
             revert PythErrors.InvalidGovernanceMessage();
         }
@@ -131,11 +156,8 @@ abstract contract PythGovernance is
         // Make sure the claimVaa is a valid VAA with RequestGovernanceDataSourceTransfer governance message
         // If it's valid then its emitter can take over the governance from the current emitter.
         // The VAA is checked here to ensure that the new governance data source is valid and can send message
-        // through wormhole.
-        (IWormhole.VM memory vm, bool valid, ) = wormhole().parseAndVerifyVM(
-            payload.claimVaa
-        );
-        if (!valid) revert PythErrors.InvalidWormholeVaa();
+        // through wormhole or verifier.
+        IWormhole.VM memory vm = validateVm(payload.claimVaa);
 
         GovernanceInstruction memory gi = parseGovernanceInstruction(
             vm.payload
@@ -210,6 +232,8 @@ abstract contract PythGovernance is
         emit ValidPeriodSet(oldValidPeriod, validTimePeriodSeconds());
     }
 
+    // If the VAA was created by Verifier, this will revert,
+    // because it assumes that the new Wormhole is able to parse and verify the governance VAA.
     function setWormholeAddress(
         SetWormholeAddressPayload memory payload,
         bytes memory encodedVM
@@ -270,4 +294,48 @@ abstract contract PythGovernance is
 
         emit FeeWithdrawn(payload.targetAddress, payload.fee);
     }
+
+    // If the VAA was created by Wormhole, this will revert,
+    // because it assumes that the new Verifier is able to parse and verify the governance VAA.
+    function setVerifierAddress(
+        SetVerifierAddressPayload memory payload,
+        bytes memory encodedVM
+    ) internal {
+        address oldVerifierAddress = address(verifier());
+        setVerifier(payload.newVerifierAddress);
+
+        // We want to verify that the new verifier address is valid, so we make sure that it can
+        // parse and verify the same governance VAA that is used to set it.
+        (IWormhole.VM memory vm, bool valid, ) = verifier().parseAndVerifyVM(
+            encodedVM
+        );
+
+        if (!valid) revert PythErrors.InvalidGovernanceMessage();
+
+        if (!isValidGovernanceDataSource(vm.emitterChainId, vm.emitterAddress))
+            revert PythErrors.InvalidGovernanceMessage();
+
+        if (vm.sequence != lastExecutedGovernanceSequence())
+            revert PythErrors.InvalidVerifierAddressToSet();
+
+        GovernanceInstruction memory gi = parseGovernanceInstruction(
+            vm.payload
+        );
+
+        if (gi.action != GovernanceAction.SetVerifierAddress)
+            revert PythErrors.InvalidVerifierAddressToSet();
+
+        // Purposefully, we don't check whether the chainId is the same as the current chainId because
+        // we might want to change the chain id of the verifier contract.
+
+        // The following check is not necessary for security, but is a sanity check that the new verifier
+        // contract parses the payload correctly.
+        SetVerifierAddressPayload
+            memory newPayload = parseSetVerifierAddressPayload(gi.payload);
+
+        if (newPayload.newVerifierAddress != payload.newVerifierAddress)
+            revert PythErrors.InvalidVerifierAddressToSet();
+
+        emit VerifierAddressSet(oldVerifierAddress, address(verifier()));
+    }
 }

+ 19 - 1
target_chains/ethereum/contracts/contracts/pyth/PythGovernanceInstructions.sol

@@ -37,7 +37,8 @@ contract PythGovernanceInstructions {
         SetWormholeAddress, // 6
         SetFeeInToken, // 7 - No-op for EVM chains
         SetTransactionFee, // 8
-        WithdrawFee // 9
+        WithdrawFee, // 9
+        SetVerifierAddress // 10
     }
 
     struct GovernanceInstruction {
@@ -90,6 +91,10 @@ contract PythGovernanceInstructions {
         uint256 fee;
     }
 
+    struct SetVerifierAddressPayload {
+        address newVerifierAddress;
+    }
+
     /// @dev Parse a GovernanceInstruction
     function parseGovernanceInstruction(
         bytes memory encodedInstruction
@@ -272,4 +277,17 @@ contract PythGovernanceInstructions {
         if (encodedPayload.length != index)
             revert PythErrors.InvalidGovernanceMessage();
     }
+
+    /// @dev Parse a UpdateVerifierAddressPayload (action 10) with minimal validation
+    function parseSetVerifierAddressPayload(
+        bytes memory encodedPayload
+    ) public pure returns (SetVerifierAddressPayload memory sv) {
+        uint index = 0;
+
+        sv.newVerifierAddress = address(encodedPayload.toAddress(index));
+        index += 20;
+
+        if (encodedPayload.length != index)
+            revert PythErrors.InvalidGovernanceMessage();
+    }
 }

+ 4 - 0
target_chains/ethereum/contracts/contracts/pyth/PythSetters.sol

@@ -52,4 +52,8 @@ contract PythSetters is PythState, IPythEvents {
     function setTransactionFeeInWei(uint fee) internal {
         _state.transactionFeeInWei = fee;
     }
+
+    function setVerifier(address vf) internal {
+        _state.verifier = payable(vf);
+    }
 }

+ 2 - 0
target_chains/ethereum/contracts/contracts/pyth/PythState.sol

@@ -40,6 +40,8 @@ contract PythStorage {
         mapping(bytes32 => PythInternalStructs.PriceInfo) latestPriceInfo;
         // Fee charged per transaction, in addition to per-update fees
         uint transactionFeeInWei;
+        // Verifier address for verifying VAA signatures
+        address verifier;
     }
 }
 

+ 4 - 2
target_chains/ethereum/contracts/contracts/pyth/PythUpgradable.sol

@@ -28,7 +28,8 @@ contract PythUpgradable is
         bytes32 governanceEmitterAddress,
         uint64 governanceInitialSequence,
         uint validTimePeriodSeconds,
-        uint singleUpdateFeeInWei
+        uint singleUpdateFeeInWei,
+        address verifier
     ) public initializer {
         __Ownable_init();
         __UUPSUpgradeable_init();
@@ -41,7 +42,8 @@ contract PythUpgradable is
             governanceEmitterAddress,
             governanceInitialSequence,
             validTimePeriodSeconds,
-            singleUpdateFeeInWei
+            singleUpdateFeeInWei,
+            verifier
         );
 
         renounceOwnership();

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

@@ -49,4 +49,7 @@ library PythErrors {
     error InvalidTwapUpdateData();
     // The twap update data set is invalid.
     error InvalidTwapUpdateDataSet();
+    // The verifier address to set in SetVerifierAddress governance is invalid.
+    // Signature: 0xab8af376
+    error InvalidVerifierAddressToSet();
 }

+ 5 - 0
target_chains/ethereum/sdk/solidity/abis/PythErrors.json

@@ -44,6 +44,11 @@
     "name": "InvalidUpdateDataSource",
     "type": "error"
   },
+  {
+    "inputs": [],
+    "name": "InvalidVerifierAddressToSet",
+    "type": "error"
+  },
   {
     "inputs": [],
     "name": "InvalidWormholeAddressToSet",