Skip to content

Combined contract#2

Draft
javerett wants to merge 59 commits intononergodic:masterfrom
XLabs:combined-contract
Draft

Combined contract#2
javerett wants to merge 59 commits intononergodic:masterfrom
XLabs:combined-contract

Conversation

@javerett
Copy link
Copy Markdown

No description provided.

Comment thread 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) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Comment thread src/GuardianSetVerification.sol Outdated
Comment thread src/GuardianSetVerification.sol Outdated
Comment on lines +155 to +159
function eagerOr(bool lhs, bool rhs) private pure returns (bool ret) {
assembly ("memory-safe") {
ret := or(lhs, rhs)
}
}
Copy link
Copy Markdown
Owner

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.

Comment thread src/GuardianSetVerification.sol Outdated
Comment thread src/GuardianSetVerification.sol Outdated
}

// Write the guardian set to the ExtStore and verify the index
uint32 extIndex = uint32(_extWrite(data));
Copy link
Copy Markdown
Owner

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.

Comment thread src/VerificationV2.sol Outdated
result_entry = abi.encodePacked(expirationTime, guardianSetAddrs);
}

result = abi.encodePacked(result, result_entry);
Copy link
Copy Markdown
Owner

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).

Comment thread src/VerificationV2.sol Outdated
Comment on lines +110 to +112
uint8 version;
(version, offset) = data.asUint8CdUnchecked(offset);
if (version != RAW_DISPATCH_PROTOCOL_VERSION) revert InvalidDispatchVersion(version);
Copy link
Copy Markdown
Owner

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.

Comment thread src/VerificationV2.sol Outdated
if (version != RAW_DISPATCH_PROTOCOL_VERSION) revert InvalidDispatchVersion(version);

bytes memory result;
uint length = data.length;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above

Comment thread src/VerificationV2.sol Outdated
Comment on lines +79 to +83
uint32 dataLength;
(dataLength, offset) = data.asUint32CdUnchecked(offset);

bytes calldata encodedVaa = data[offset:offset + dataLength];
VaaBody memory vaaBody = decodeAndVerifyVaa(encodedVaa);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BytesParsing.sliceUint16PrefixedCdUnchecked

Comment thread src/VerificationV2.sol Outdated
Comment thread src/GuardianSetVerification.sol Outdated
using VaaLib for bytes;
using UncheckedIndexing for address[];

ICoreBridge private _coreV1;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ICoreBridge private _coreV1;
ICoreBridge private immutable _coreBridge;

V1 suggests that there is a v2 when really there is only a v2 for verification.

Comment thread 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) {
Copy link
Copy Markdown
Owner

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

Comment thread src/GuardianSetVerification.sol Outdated
Copy link
Copy Markdown
Owner

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)

Comment thread 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++) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (uint i = oldGuardianSetLength; i < upper; i++) {
for (uint i = oldGuardianSetLength; i < upper; ++i) {

nit for consistency

Comment thread src/ThresholdVerification.sol Outdated
// address (160 bits)
uint256[] private _pastThresholdInfo;

bytes32[][] private _shards;
Copy link
Copy Markdown
Owner

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" ...

Comment thread src/VerificationV2.sol Outdated
Comment on lines +99 to +107
(
,
,
,
,
,
,
bytes calldata payload
) = _decodeAndVerifyVaa(encodedVaa);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤨

Copy link
Copy Markdown
Author

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

Comment thread src/VerificationV2.sol Outdated
bytes calldata encodedVaa;
(encodedVaa, offset) = data.sliceUint16PrefixedCdUnchecked(offset);

// Decode the VAA
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Decode the VAA
// Decode and verify the VAA

and arguably the comments aren't really adding anything in this function

Comment thread src/VerificationV2.sol Outdated
uint8 shardsLength;
(shardsLength, offset) = payload.asUint8MemUnchecked(offset);
shards = new bytes32[](shardsLength);
for (uint i = 0; i < shardsLength; i++) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (uint i = 0; i < shardsLength; i++) {
for (uint i = 0; i < shardsLength; ++i) {

Comment thread src/VerificationV2.sol Outdated
Comment on lines +119 to +120
if (module != MODULE_VERIFICATION_V2) revert InvalidModule(module);
if (action != ACTION_APPEND_THRESHOLD_KEY) revert InvalidAction(action);
Copy link
Copy Markdown
Owner

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.

Comment thread src/WormholeVerifier.sol Outdated
Copy link
Copy Markdown
Owner

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?)

Copy link
Copy Markdown
Author

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

Comment thread src/solana/programs/solana/src/vaa.rs Outdated
pub struct VAA {
// Header
pub version: u8,
pub guardian_set_index: u32,
Copy link
Copy Markdown
Owner

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

Comment thread src/solana/programs/solana/src/vaa.rs Outdated
Comment on lines +28 to +29
pub fn get_seed_bytes(encoded_vaa: &[u8]) -> &[u8] {
// Extract the index from the encoded VAA
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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] {

Comment thread src/solana/programs/solana/src/lib.rs Outdated
#[derive(InitSpace)]
pub struct ThresholdKey {
pub bump: u8,
pub index: u32, // TODO: Is this needed?
Copy link
Copy Markdown
Owner

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.

Comment thread src/solana/programs/solana/src/lib.rs Outdated
Ok(())
}

pub fn verify_vaa(ctx: Context<VerifyVaa>, raw_vaa: Vec<u8>) -> Result<VAA> {
Copy link
Copy Markdown
Owner

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.

Comment thread src/solana/programs/solana/src/vaa.rs Outdated
let mut payload = Vec::new();
cursor.read_to_end(&mut payload)?;

let double_hash = hash(&hash(&data[body_start..]).to_bytes()).to_bytes();
Copy link
Copy Markdown
Owner

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.

Comment thread src/solana/programs/solana/src/lib.rs Outdated
Comment on lines +163 to +176
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());
}
Copy link
Copy Markdown
Owner

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.

Comment thread src/solana/programs/solana/src/lib.rs Outdated
Comment on lines +113 to +119
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());
}
Copy link
Copy Markdown
Owner

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.

Comment thread src/solana/programs/solana/src/lib.rs Outdated
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])]
Copy link
Copy Markdown
Owner

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.

Copy link
Copy Markdown
Owner

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.

Comment thread src/solana/programs/solana/src/lib.rs Outdated
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])]
Copy link
Copy Markdown
Owner

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.

Comment thread src/solana/programs/solana/src/lib.rs Outdated
Comment on lines +130 to +142
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());
}
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eliminate as mentioned above.

JoshuaAverett and others added 29 commits May 12, 2025 15:27
This makes the semantics of the append threshold key VAA identical to
the Solana implementation.
@scnale scnale deleted the combined-contract branch June 5, 2025 20:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants