-
Notifications
You must be signed in to change notification settings - Fork 267
Add verifier to pyth evm contract #2784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e6da773
64786b9
0e370d0
9254cbc
9854e9c
09f2529
f28985d
7781629
0bc888b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,24 +29,44 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth { | |
TwapPriceFeed | ||
} | ||
|
||
enum Signer { | ||
Wormhole, | ||
Verifier | ||
} | ||
|
||
// This method is also used by batch attestation but moved here | ||
// as the batch attestation will deprecate soon. | ||
function parseAndVerifyPythVM( | ||
bytes calldata encodedVm | ||
bytes calldata encodedVm, | ||
Signer signer | ||
) internal view returns (IWormhole.VM memory vm) { | ||
{ | ||
bool valid; | ||
bool valid; | ||
if (signer == Signer.Wormhole) { | ||
(vm, valid, ) = wormhole().parseAndVerifyVM(encodedVm); | ||
if (!valid) revert PythErrors.InvalidWormholeVaa(); | ||
} else if (signer == Signer.Verifier) { | ||
if (address(verifier()) == address(0)) | ||
revert PythErrors.InvalidUpdateData(); | ||
(vm, valid, ) = verifier().parseAndVerifyVM(encodedVm); | ||
} else { | ||
revert PythErrors.InvalidSigner(); | ||
} | ||
|
||
if (!valid) { | ||
revert PythErrors.InvalidUpdateData(); | ||
} | ||
|
||
if (!isValidDataSource(vm.emitterChainId, vm.emitterAddress)) | ||
if (!isValidDataSource(vm.emitterChainId, vm.emitterAddress)) { | ||
revert PythErrors.InvalidUpdateDataSource(); | ||
} | ||
} | ||
|
||
function extractUpdateTypeFromAccumulatorHeader( | ||
function extractAccumulatorHeaderDetails( | ||
bytes calldata accumulatorUpdate | ||
) internal pure returns (uint offset, UpdateType updateType) { | ||
) | ||
internal | ||
pure | ||
returns (uint offset, UpdateType updateType, Signer signer) | ||
{ | ||
unchecked { | ||
offset = 0; | ||
|
||
|
@@ -90,13 +110,14 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth { | |
); | ||
offset += 1; | ||
|
||
// We use another offset for the trailing header and in the end add the | ||
// offset by trailingHeaderSize to skip the future headers. | ||
// | ||
// An example would be like this: | ||
// uint trailingHeaderOffset = offset | ||
// uint x = UnsafeBytesLib.ToUint8(accumulatorUpdate, trailingHeaderOffset) | ||
// trailingHeaderOffset += 1 | ||
uint trailingHeaderOffset = offset; | ||
signer = Signer( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i've lost my solidity knowledge here, what would happen if this byte does not fit in Signer (which accepts only 0 and 1). let's check it if it's an undefined behaviour. |
||
UnsafeCalldataBytesLib.toUint8( | ||
accumulatorUpdate, | ||
trailingHeaderOffset | ||
) | ||
); | ||
trailingHeaderOffset += 1; | ||
|
||
offset += trailingHeaderSize; | ||
} | ||
|
@@ -112,8 +133,9 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth { | |
} | ||
} | ||
|
||
function extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedAndSlotFromAccumulatorUpdate( | ||
function extractWormholeMerkleHeader( | ||
bytes calldata accumulatorUpdate, | ||
Signer signer, | ||
uint encodedOffset | ||
) | ||
internal | ||
|
@@ -148,7 +170,8 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth { | |
encoded, | ||
offset, | ||
whProofSize | ||
) | ||
), | ||
signer | ||
); | ||
offset += whProofSize; | ||
|
||
|
@@ -171,7 +194,7 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth { | |
UpdateType updateType = UpdateType( | ||
UnsafeBytesLib.toUint8(encodedPayload, payloadOffset) | ||
); | ||
++payloadOffset; | ||
payloadOffset += 1; | ||
|
||
if (updateType != UpdateType.WormholeMerkle) | ||
revert PythErrors.InvalidUpdateData(); | ||
|
@@ -460,8 +483,9 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth { | |
) internal returns (uint8 numUpdates) { | ||
( | ||
uint encodedOffset, | ||
UpdateType updateType | ||
) = extractUpdateTypeFromAccumulatorHeader(accumulatorUpdate); | ||
UpdateType updateType, | ||
Signer signer | ||
) = extractAccumulatorHeaderDetails(accumulatorUpdate); | ||
|
||
if (updateType != UpdateType.WormholeMerkle) { | ||
revert PythErrors.InvalidUpdateData(); | ||
|
@@ -477,8 +501,9 @@ abstract contract PythAccumulator is PythGetters, PythSetters, AbstractPyth { | |
numUpdates, | ||
encoded, | ||
slot | ||
) = extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedAndSlotFromAccumulatorUpdate( | ||
) = extractWormholeMerkleHeader( | ||
accumulatorUpdate, | ||
signer, | ||
encodedOffset | ||
); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,7 +37,8 @@ contract PythGovernanceInstructions { | |
SetWormholeAddress, // 6 | ||
SetFeeInToken, // 7 - No-op for EVM chains | ||
SetTransactionFee, // 8 | ||
WithdrawFee // 9 | ||
WithdrawFee, // 9 | ||
SetVerifierAddress // 10 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note for the future: we need to support it in governance sdk in TS |
||
} | ||
|
||
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(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shall we change
MINIMUM_ALLOWED_MINOR_VERSION
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to change that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is a metadata to tell us on which version we are right now and reject messages with older minor version. if we don't check it and this gets upgraded before Hermes there might be problems.