wormhole-circle-integration/solana/modules/wormhole-cctp/src/wormhole/core_bridge_program/vaa/zero_copy/posted_vaa_v1.rs

116 lines
3.8 KiB
Rust

use std::cell::Ref;
use anchor_lang::{
prelude::{
error, require, require_eq, require_keys_eq, AccountInfo, ErrorCode, Pubkey, Result,
},
solana_program::keccak,
};
pub const POSTED_VAA_V1_SEED_PREFIX: &[u8] = b"PostedVAA";
const PAYLOAD_START: usize = 95;
/// Account used to store a verified VAA.
pub struct PostedVaaV1<'a>(Ref<'a, &'a mut [u8]>);
impl<'a> PostedVaaV1<'a> {
/// Level of consistency requested by the emitter.
pub fn consistency_level(&self) -> u8 {
self.0[4]
}
/// Time the message was submitted.
pub fn timestamp(&self) -> u32 {
u32::from_le_bytes(self.0[5..9].try_into().unwrap())
}
#[cfg(feature = "no-entrypoint")]
/// Pubkey of `SignatureSet` account that represent this VAA's signature verification.
pub fn signature_set(&self) -> Pubkey {
Pubkey::try_from(&self.0[9..41]).unwrap()
}
/// Guardian set index used to verify signatures for `SignatureSet`.
///
/// NOTE: In the previous implementation, this member was referred to as the `posted_timestamp`,
/// which is zero for VAA data (posted messages and VAAs resemble the same account schema). By
/// changing this to the guardian set index, we patch a bug with verifying governance VAAs for
/// the Core Bridge (other Core Bridge implementations require that the guardian set that
/// attested for the governance VAA is the current one).
pub fn guardian_set_index(&self) -> u32 {
u32::from_le_bytes(self.0[41..45].try_into().unwrap())
}
/// Unique ID for this message.
pub fn nonce(&self) -> u32 {
u32::from_le_bytes(self.0[45..49].try_into().unwrap())
}
/// Sequence number of this message.
pub fn sequence(&self) -> u64 {
u64::from_le_bytes(self.0[49..57].try_into().unwrap())
}
/// The Wormhole chain ID denoting the origin of this message.
pub fn emitter_chain(&self) -> u16 {
u16::from_le_bytes(self.0[57..59].try_into().unwrap())
}
/// Emitter of the message.
pub fn emitter_address(&self) -> [u8; 32] {
self.0[59..91].try_into().unwrap()
}
pub fn payload_size(&self) -> usize {
u32::from_le_bytes(self.0[91..PAYLOAD_START].try_into().unwrap())
.try_into()
.unwrap()
}
/// Message payload.
pub fn payload(&self) -> &[u8] {
&self.0[PAYLOAD_START..]
}
/// Recompute the message hash, which is used derive the [PostedVaaV1] PDA address.
pub fn message_hash(&self) -> keccak::Hash {
keccak::hashv(&[
self.timestamp().to_be_bytes().as_ref(),
self.nonce().to_be_bytes().as_ref(),
self.emitter_chain().to_be_bytes().as_ref(),
&self.emitter_address(),
&self.sequence().to_be_bytes(),
&[self.consistency_level()],
self.payload(),
])
}
/// Compute digest (hash of [message_hash](Self::message_hash)).
pub fn digest(&self) -> keccak::Hash {
keccak::hash(self.message_hash().as_ref())
}
pub(super) fn new(acc_info: &'a AccountInfo) -> Result<Self> {
let parsed = Self(acc_info.try_borrow_data()?);
require!(
parsed.0.len() >= PAYLOAD_START,
ErrorCode::AccountDidNotDeserialize
);
require_eq!(
parsed.0.len(),
PAYLOAD_START + parsed.payload_size(),
ErrorCode::AccountDidNotDeserialize
);
// Recompute message hash to re-derive PDA address.
let (expected_address, _) = Pubkey::find_program_address(
&[POSTED_VAA_V1_SEED_PREFIX, parsed.message_hash().as_ref()],
&crate::wormhole::core_bridge_program::id(),
);
require_keys_eq!(*acc_info.key, expected_address, ErrorCode::ConstraintSeeds);
Ok(parsed)
}
}