-
Notifications
You must be signed in to change notification settings - Fork 2
Combined contract #2
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: master
Are you sure you want to change the base?
Conversation
src/GuardianSetVerification.sol
Outdated
| function pullGuardianSets() public { | ||
| // For each guardian set after the current one | ||
| uint32 coreGuardianSetIndex = _coreV1.getCurrentGuardianSetIndex(); | ||
| for (uint32 i = uint32(_guardianSetExpirationTime.length); i <= coreGuardianSetIndex; ++i) { |
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.
| for (uint32 i = uint32(_guardianSetExpirationTime.length); i <= coreGuardianSetIndex; ++i) { | |
| for (uint i = _guardianSetExpirationTime.length; i <= coreGuardianSetIndex; ++i) { |
Loop counters should always be uint(256). Using a different datatype only increases gas cost and you'll never overflow the boundary anyway.
src/GuardianSetVerification.sol
Outdated
| function eagerOr(bool lhs, bool rhs) private pure returns (bool ret) { | ||
| assembly ("memory-safe") { | ||
| ret := or(lhs, rhs) | ||
| } | ||
| } |
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.
This is available in the utils of the Solidity SDK and should be taken from there.
src/GuardianSetVerification.sol
Outdated
| } | ||
|
|
||
| // Write the guardian set to the ExtStore and verify the index | ||
| uint32 extIndex = uint32(_extWrite(data)); |
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.
No need for casts once the loop counter is simply a uint.
src/VerificationV2.sol
Outdated
| result_entry = abi.encodePacked(expirationTime, guardianSetAddrs); | ||
| } | ||
|
|
||
| result = abi.encodePacked(result, result_entry); |
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.
Instead of doing it only once here, this should be copy pasted to each individual branch and combined with the abi.encodePacked of that branch. solc is sadly not smart enough to do this sort of thing so this will incur an additional, unnecessary, looping memcopy (looping because we likely can't rely on he MEMCOPY opcode to exist everywhere we deploy this contract).
src/VerificationV2.sol
Outdated
| uint8 version; | ||
| (version, offset) = data.asUint8CdUnchecked(offset); | ||
| if (version != RAW_DISPATCH_PROTOCOL_VERSION) revert InvalidDispatchVersion(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.
likewise no need for a version.
src/VerificationV2.sol
Outdated
| if (version != RAW_DISPATCH_PROTOCOL_VERSION) revert InvalidDispatchVersion(version); | ||
|
|
||
| bytes memory result; | ||
| uint length = data.length; |
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.
see above
src/VerificationV2.sol
Outdated
| uint32 dataLength; | ||
| (dataLength, offset) = data.asUint32CdUnchecked(offset); | ||
|
|
||
| bytes calldata encodedVaa = data[offset:offset + dataLength]; | ||
| VaaBody memory vaaBody = decodeAndVerifyVaa(encodedVaa); |
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.
BytesParsing.sliceUint16PrefixedCdUnchecked
src/GuardianSetVerification.sol
Outdated
| using VaaLib for bytes; | ||
| using UncheckedIndexing for address[]; | ||
|
|
||
| ICoreBridge private _coreV1; |
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.
| ICoreBridge private _coreV1; | |
| ICoreBridge private immutable _coreBridge; |
V1 suggests that there is a v2 when really there is only a v2 for verification.
src/GuardianSetVerification.sol
Outdated
| uint32[] private _guardianSetExpirationTime; | ||
|
|
||
| // Get the guardian addresses for a given guardian set index using the ExtStore | ||
| function getGuardianSetInfo(uint32 index) public view returns (uint32 expirationTime, address[] memory guardianAddrs) { |
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.
Properly functioning code should never create a Panic, not even on invalid external input.
from the Solidity documentation
src/GuardianSetVerification.sol
Outdated
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.
File should use spaces, not tabs (and my preference is 2 space indentation, but whatever) and a max line length of 100 (except for urls that can't be line-broken)
src/GuardianSetVerification.sol
Outdated
| uint upper = (limit == 0 || currentGuardianSetLength - oldGuardianSetLength < limit) ? currentGuardianSetLength : oldGuardianSetLength + limit; | ||
|
|
||
| // Pull and append the guardian sets | ||
| for (uint i = oldGuardianSetLength; i < upper; i++) { |
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.
| for (uint i = oldGuardianSetLength; i < upper; i++) { | |
| for (uint i = oldGuardianSetLength; i < upper; ++i) { |
nit for consistency
src/ThresholdVerification.sol
Outdated
| // address (160 bits) | ||
| uint256[] private _pastThresholdInfo; | ||
|
|
||
| bytes32[][] private _shards; |
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.
This connection is currently communicated
Oh... this should have said "is currently NOT communicated" ...
src/VerificationV2.sol
Outdated
| ( | ||
| , | ||
| , | ||
| , | ||
| , | ||
| , | ||
| , | ||
| bytes calldata payload | ||
| ) = _decodeAndVerifyVaa(encodedVaa); |
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.
🤨
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 could go on one line if you like? I found this a bit easier to read, but it's still not pretty haha
src/VerificationV2.sol
Outdated
| bytes calldata encodedVaa; | ||
| (encodedVaa, offset) = data.sliceUint16PrefixedCdUnchecked(offset); | ||
|
|
||
| // Decode the VAA |
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.
| // Decode the VAA | |
| // Decode and verify the VAA |
and arguably the comments aren't really adding anything in this function
src/VerificationV2.sol
Outdated
| uint8 shardsLength; | ||
| (shardsLength, offset) = payload.asUint8MemUnchecked(offset); | ||
| shards = new bytes32[](shardsLength); | ||
| for (uint i = 0; i < shardsLength; i++) { |
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.
| for (uint i = 0; i < shardsLength; i++) { | |
| for (uint i = 0; i < shardsLength; ++i) { |
src/VerificationV2.sol
Outdated
| if (module != MODULE_VERIFICATION_V2) revert InvalidModule(module); | ||
| if (action != ACTION_APPEND_THRESHOLD_KEY) revert InvalidAction(action); |
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.
These 2 checks should be done in the decode function imho. exec should only be doing parsing and dispatching work, imho.
src/WormholeVerifier.sol
Outdated
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.
errors can be standalone (don't have to be declared inside a contract) and I'm not sure if you can't just declare them twice (presumably you moved it here because you can't?)
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.
Yep, it complains about redefinition
… combined-contract
| pub struct VAA { | ||
| // Header | ||
| pub version: u8, | ||
| pub guardian_set_index: u32, |
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.
should be tss_index or something similar
| pub fn get_seed_bytes(encoded_vaa: &[u8]) -> &[u8] { | ||
| // Extract the index from the encoded VAA |
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.
| pub fn get_seed_bytes(encoded_vaa: &[u8]) -> &[u8] { | |
| // Extract the index from the encoded VAA | |
| pub fn get_tss_index(encoded_vaa: &[u8]) -> &[u8] { |
| #[derive(InitSpace)] | ||
| pub struct ThresholdKey { | ||
| pub bump: u8, | ||
| pub index: u32, // TODO: Is this needed? |
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.
Yes, keep this and drop the bump. One can then verify that the account is right by just checking that the owner is the oracle, that the discriminator matches (both done automatically by anchor) and the tss_index can be used to verify that it's the right address too.
| Ok(()) | ||
| } | ||
|
|
||
| pub fn verify_vaa(ctx: Context<VerifyVaa>, raw_vaa: Vec<u8>) -> Result<VAA> { |
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.
You can have this as an instruction, but the functionality itself should come from a library. There's no reason to force users to do a precious CPI for something that they can just as easily just verify themselves.
| let mut payload = Vec::new(); | ||
| cursor.read_to_end(&mut payload)?; | ||
|
|
||
| let double_hash = hash(&hash(&data[body_start..]).to_bytes()).to_bytes(); |
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.
I'm not convinced you should be returning any hash here, and if you do, it should definitely be hashed only once. Double hashing was a mistake in the old EVM core bridge, but everywhere else, the hash of a VAA is always considered the single hash (e.g. Solana core bridge impl).
This is also documented in the TS-SDK, the Solidity SDK, and the docs.
| if vaa.version != 2 { | ||
| return Err(VAAError::InvalidVersion.into()); | ||
| } | ||
|
|
||
| // Check if the threshold key index matches the VAA index | ||
| let threshold_key = &mut ctx.accounts.threshold_key; | ||
| if threshold_key.index != vaa.guardian_set_index { | ||
| return Err(VAAError::InvalidIndex.into()); | ||
| } | ||
|
|
||
| // Check if the threshold key has expired | ||
| if threshold_key.expiration != 0 && threshold_key.expiration < Clock::get().unwrap().unix_timestamp as u64 { | ||
| return Err(VAAError::GuardianSetExpired.into()); | ||
| } |
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.
Move checks to accounts struct if possible.
| if vaa.meta.emitter_chain != CHAIN_ID_SOLANA { | ||
| return Err(AppendThresholdKeyError::InvalidGovernanceChainId.into()); | ||
| } | ||
|
|
||
| if vaa.meta.emitter_address != GOVERNANCE_ADDRESS { | ||
| return Err(AppendThresholdKeyError::InvalidGovernanceAddress.into()); | ||
| } |
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.
Move checks to accounts struct.
| const GOVERNANCE_ADDRESS: [u8; 32] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4]; | ||
|
|
||
| #[derive(Accounts)] | ||
| #[instruction(new_index: u32, vaa_hash: [u8; 32])] |
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.
what's the purpose of the vaa_hash value here? The derivation doesn't guarantee anything, does it? Seems superfluous.
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.
Yes, again, you should be able to take inspiration from TBRv3. It defines a type PostedRelayerMessage which it uses like so.
So you in turn can just use the analogous type from the core bridge for your governance message.
| const GOVERNANCE_ADDRESS: [u8; 32] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4]; | ||
|
|
||
| #[derive(Accounts)] | ||
| #[instruction(new_index: u32, vaa_hash: [u8; 32])] |
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.
Likewise, there should be no need to pass in the new_index either. If the optional account does not exist, the index must be 0, otherwise you can take it from the account and add +1.
| if new_index == 0 { | ||
| if ctx.accounts.old_threshold_key.is_some() { | ||
| return Err(AppendThresholdKeyError::InvalidOldThresholdKey.into()); | ||
| } | ||
| } else { | ||
| if let Some(old_threshold_key) = &ctx.accounts.old_threshold_key { | ||
| if old_threshold_key.index != new_index - 1 { | ||
| return Err(AppendThresholdKeyError::InvalidOldThresholdKey.into()); | ||
| } | ||
| } else { | ||
| return Err(AppendThresholdKeyError::InvalidOldThresholdKey.into()); | ||
| } | ||
| } |
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.
Eliminate as mentioned above.
… combined-contract
These set a bogus discriminator for `PostedVaaData`. The discriminator is only used to emit the IDL so it's safe in terms of contract code. The IDL should be fixed afterwards probably.
… combined-contract
This makes the semantics of the append threshold key VAA identical to the Solana implementation.
… combined-contract
… combined-contract
No description provided.