//! VAA's represent a collection of signatures combined with a message and its metadata. VAA's are //! used as a form of proof; by submitting a VAA to a target contract, the receiving contract can //! make assumptions about the validity of state on the source chain. //! //! Wormhole defines several VAA's for use within Token/NFT bridge implemenetations, as well as //! governance specific VAA's used within Wormhole's guardian network. //! //! This module provides definitions and parsers for all current Wormhole standard VAA's, and //! includes parsers for the core VAA type. Programs targetting wormhole can use this module to //! parse and verify incoming VAA's securely. use nom::combinator::rest; use nom::error::{Error, ErrorKind}; use nom::multi::{count, fill}; use nom::number::complete::{u16, u32, u64, u8}; use nom::number::Endianness; use nom::{Err, Finish, IResult}; use std::convert::TryFrom; use crate::{ require, Chain, WormholeError::{ self, InvalidGovernanceAction, InvalidGovernanceChain, InvalidGovernanceModule, }, }; // Import Module Specific VAAs. pub mod core; pub mod nft; pub mod token; /// Signatures are typical ECDSA signatures prefixed with a Guardian position. These have the /// following byte layout: /// ```markdown /// 0 .. 1: Guardian No. /// 1 .. 65: Signature (ECDSA) /// 65 .. 66: Recovery ID (ECDSA) /// ``` pub type Signature = [u8; 66]; /// Wormhole specifies token addresses as 32 bytes. Addresses that are shorter, for example 20 byte /// Ethereum addresses, are left zero padded to 32. pub type ForeignAddress = [u8; 32]; /// Fields on VAA's are all usually fixed bytestrings, however they often contain UTF-8. When /// parsed these result in `String` with the additional constraint that they are always equal or /// less to the underlying byte field. type ShortUTFString = String; /// The core VAA itself. This structure is what is received by a contract on the receiving side of /// a wormhole message passing flow. The payload of the message must be parsed separately to the /// VAA itself as it is completely user defined. #[derive(Debug, Default, PartialEq)] pub struct VAA { // Header pub version: u8, pub guardian_set_index: u32, pub signatures: Vec, // Body pub timestamp: u32, pub nonce: u32, pub emitter_chain: Chain, pub emitter_address: ForeignAddress, pub sequence: u64, pub consistency_level: u8, pub payload: Vec, } /// Contains the hash, secp256k1 payload, and serialized digest of the VAA. These are used in /// various places in Wormhole codebases. pub struct VAADigest { pub digest: Vec, pub hash: [u8; 32], } impl VAA { /// Given any argument treatable as a series of bytes, attempt to deserialize into a valid VAA. pub fn from_bytes>(input: T) -> Result { match parse_vaa(input.as_ref()).finish() { Ok(input) => Ok(input.1), Err(e) => Err(WormholeError::ParseError(e.code as usize)), } } /// A VAA is distinguished by the unique hash of its deterministic components. This method /// returns a 256 bit Keccak hash of these components. This hash is utilised in all Wormhole /// components for identifying unique VAA's, including the bridge, modules, and core guardian /// software. pub fn digest(&self) -> Option { use byteorder::{BigEndian, WriteBytesExt}; use sha3::Digest; use std::io::{Cursor, Write}; // Hash Deterministic Pieces let body = { let mut v = Cursor::new(Vec::new()); v.write_u32::(self.timestamp).ok()?; v.write_u32::(self.nonce).ok()?; v.write_u16::(self.emitter_chain.clone() as u16) .ok()?; let _ = v.write(&self.emitter_address).ok()?; v.write_u64::(self.sequence).ok()?; v.write_u8(self.consistency_level).ok()?; let _ = v.write(&self.payload).ok()?; v.into_inner() }; // We hash the body so that secp256k1 signatures are signing the hash instead of the body // within our contracts. We do this so we don't have to submit the entire VAA for signature // verification, only the hash. let hash: [u8; 32] = { let mut h = sha3::Keccak256::default(); let _ = h.write(body.as_slice()).unwrap(); h.finalize().into() }; Some(VAADigest { digest: body, hash }) } } /// Using nom, parse a fixed array of bytes without any allocation. Useful for parsing addresses, /// signatures, identifiers, etc. #[inline] pub fn parse_fixed(input: &[u8]) -> IResult<&[u8], [u8; S]> { let mut buffer = [0u8; S]; let (i, _) = fill(u8, &mut buffer)(input)?; Ok((i, buffer)) } /// Parse a Chain ID, which is a 16 bit numeric ID. The mapping of network to ID is defined by the /// Wormhole standard. #[inline] pub fn parse_chain(input: &[u8]) -> IResult<&[u8], Chain> { let (i, chain) = u16(Endianness::Big)(input)?; let chain = Chain::try_from(chain).map_err(|_| Err::Error(Error::new(i, ErrorKind::NoneOf)))?; Ok((i, chain)) } /// Parse a VAA from a vector of raw bytes. Nom handles situations where the data is either too /// short or too long. #[inline] fn parse_vaa(input: &[u8]) -> IResult<&[u8], VAA> { let (i, version) = u8(input)?; let (i, guardian_set_index) = u32(Endianness::Big)(i)?; let (i, signature_count) = u8(i)?; let (i, signatures) = count(parse_fixed, signature_count.into())(i)?; let (i, timestamp) = u32(Endianness::Big)(i)?; let (i, nonce) = u32(Endianness::Big)(i)?; let (i, emitter_chain) = parse_chain(i)?; let (i, emitter_address) = parse_fixed(i)?; let (i, sequence) = u64(Endianness::Big)(i)?; let (i, consistency_level) = u8(i)?; let (i, payload) = rest(i)?; Ok(( i, VAA { version, guardian_set_index, signatures, timestamp, nonce, emitter_chain, emitter_address, sequence, consistency_level, payload: payload.to_vec(), }, )) } /// All current Wormhole programs using Governance are prefixed with a Governance header with a /// consistent format. pub struct GovHeader { pub module: [u8; 32], pub action: u8, pub chains: Chain, } pub trait GovernanceAction: Sized { const ACTION: u8; const MODULE: &'static [u8]; /// Implement a nom parser for the Action. fn parse(input: &[u8]) -> IResult<&[u8], Self>; /// Serialize to Wormhole wire format. /// fn serialize(&self) -> Result, WormholeError>; /// Parses an Action from a governance payload securely. fn from_bytes>( input: T, chain: Option, ) -> Result<(GovHeader, Self), WormholeError> { match parse_action(input.as_ref()).finish() { Ok((_, (header, action))) => { // If no Chain is given, we assume All, which implies always valid. let chain = chain.unwrap_or(Chain::All); // Left 0-pad the MODULE in case it is unpadded. let mut module = [0u8; 32]; let modlen = Self::MODULE.len(); (&mut module[32 - modlen..]).copy_from_slice(&Self::MODULE); // Verify Governance Data. let valid_chain = chain == header.chains || chain == Chain::All; let valid_action = header.action == Self::ACTION; let valid_module = module == header.module; require!(valid_action, InvalidGovernanceAction); require!(valid_chain, InvalidGovernanceChain); require!(valid_module, InvalidGovernanceModule); Ok((header, action)) } Err(e) => Err(WormholeError::ParseError(e.code as usize)), } } } #[inline] pub fn parse_action(input: &[u8]) -> IResult<&[u8], (GovHeader, A)> { let (i, header) = parse_governance_header(input.as_ref())?; let (i, action) = A::parse(i)?; Ok((i, (header, action))) } #[inline] pub fn parse_governance_header<'i, 'a>(input: &'i [u8]) -> IResult<&'i [u8], GovHeader> { let (i, module) = parse_fixed(input)?; let (i, action) = u8(i)?; let (i, chains) = u16(Endianness::Big)(i)?; Ok(( i, GovHeader { module, action, chains: Chain::try_from(chains).unwrap(), }, )) } #[cfg(test)] mod testing { use super::{parse_governance_header, Chain, VAA}; #[test] fn test_valid_gov_header() { let module = hex::decode("000000000000000000000000000000000000000000546f6b656e427269646765") .unwrap(); // Decode VAA. let vaa = hex::decode("01000000000100b072505b5b999c1d08905c02e2b6b2832ef72c0ba6c8db4f77fe457ef2b3d053410b1e92a9194d9210df24d987ac83d7b6f0c21ce90f8bc1869de0898bda7e980100000001000000010001000000000000000000000000000000000000000000000000000000000000000400000000013c1bfa00000000000000000000000000000000000000000000546f6b656e42726964676501000000013b26409f8aaded3f5ddca184695aa6a0fa829b0c85caf84856324896d214ca98").unwrap(); let vaa = VAA::from_bytes(vaa).unwrap(); // Decode Payload let (_, header) = parse_governance_header(&vaa.payload).unwrap(); // Confirm Parsed matches Required. assert_eq!(&header.module, &module[..]); assert_eq!(header.action, 1); assert_eq!(header.chains, Chain::All); } // Legacy VAA Signature Struct. #[derive(Default, Clone)] pub struct VAASignature { pub signature: Vec, pub guardian_index: u8, } // Original VAA Parsing Code. Used to compare current code to old for parity. pub fn legacy_deserialize(data: &[u8]) -> std::result::Result { use byteorder::{BigEndian, ReadBytesExt}; use std::convert::TryFrom; use std::io::Read; let mut rdr = std::io::Cursor::new(data); let mut v = VAA { ..Default::default() }; v.version = rdr.read_u8()?; v.guardian_set_index = rdr.read_u32::()?; let len_sig = rdr.read_u8()?; let mut sigs: Vec<_> = Vec::with_capacity(len_sig as usize); for _i in 0..len_sig { let mut sig = [0u8; 66]; sig[0] = rdr.read_u8()?; rdr.read_exact(&mut sig[1..66])?; sigs.push(sig); } v.signatures = sigs; v.timestamp = rdr.read_u32::()?; v.nonce = rdr.read_u32::()?; v.emitter_chain = Chain::try_from(rdr.read_u16::()?).unwrap(); let mut emitter_address = [0u8; 32]; rdr.read_exact(&mut emitter_address)?; v.emitter_address = emitter_address; v.sequence = rdr.read_u64::()?; v.consistency_level = rdr.read_u8()?; let _ = rdr.read_to_end(&mut v.payload)?; Ok(v) } #[test] fn test_parse_vaa_parity() { // Decode VAA with old and new parsers, and compare result. let vaa = hex::decode("01000000000100b072505b5b999c1d08905c02e2b6b2832ef72c0ba6c8db4f77fe457ef2b3d053410b1e92a9194d9210df24d987ac83d7b6f0c21ce90f8bc1869de0898bda7e980100000001000000010001000000000000000000000000000000000000000000000000000000000000000400000000013c1bfa00000000000000000000000000000000000000000000546f6b656e42726964676501000000013b26409f8aaded3f5ddca184695aa6a0fa829b0c85caf84856324896d214ca98").unwrap(); let new = VAA::from_bytes(&vaa).unwrap(); let old = legacy_deserialize(&vaa).unwrap(); assert_eq!(new, old); } #[test] fn test_valid_parse_vaa() { let signers = hex::decode("00b072505b5b999c1d08905c02e2b6b2832ef72c0ba6c8db4f77fe457ef2b3d053410b1e92a9194d9210df24d987ac83d7b6f0c21ce90f8bc1869de0898bda7e9801").unwrap(); let payload = hex::decode("000000000000000000000000000000000000000000546f6b656e42726964676501000000013b26409f8aaded3f5ddca184695aa6a0fa829b0c85caf84856324896d214ca98").unwrap(); let emitter = hex::decode("0000000000000000000000000000000000000000000000000000000000000004") .unwrap(); // Decode VAA. let vaa = hex::decode("01000000000100b072505b5b999c1d08905c02e2b6b2832ef72c0ba6c8db4f77fe457ef2b3d053410b1e92a9194d9210df24d987ac83d7b6f0c21ce90f8bc1869de0898bda7e980100000001000000010001000000000000000000000000000000000000000000000000000000000000000400000000013c1bfa00000000000000000000000000000000000000000000546f6b656e42726964676501000000013b26409f8aaded3f5ddca184695aa6a0fa829b0c85caf84856324896d214ca98").unwrap(); let vaa = VAA::from_bytes(vaa).unwrap(); // Verify Decoded VAA. assert_eq!(vaa.version, 1); assert_eq!(vaa.guardian_set_index, 0); assert_eq!(vaa.signatures.len(), 1); assert_eq!(vaa.signatures[0][..], signers); assert_eq!(vaa.timestamp, 1); assert_eq!(vaa.nonce, 1); assert_eq!(vaa.emitter_chain, Chain::Solana); assert_eq!(vaa.emitter_address, emitter[..]); assert_eq!(vaa.sequence, 20_716_538); assert_eq!(vaa.consistency_level, 0); assert_eq!(vaa.payload, payload); } #[test] fn test_invalid_vaa() {} }