diff --git a/proto/agent/v1/service.proto b/proto/agent/v1/service.proto index 2cf8c24ae..e788a2a00 100644 --- a/proto/agent/v1/service.proto +++ b/proto/agent/v1/service.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package agent.v1; +import "google/protobuf/timestamp.proto"; // TODO: documentation option go_package = "github.com/certusone/wormhole/bridge/pkg/proto/agent/v1;agentv1"; @@ -15,10 +16,27 @@ message Empty { } message SubmitVAARequest { - bytes vaa = 1; + VAA vaa = 1; bool skip_preflight = 2; } +message VAA { + uint32 Version = 1; + google.protobuf.Timestamp Timestamp = 2; + uint32 Nonce = 3; + uint32 EmitterChain = 4; + bytes EmitterAddress = 5; + uint64 Sequence = 6; + bytes Payload = 7; + uint32 GuardianSetIndex = 8; + repeated Signature Signatures = 9; +} + +message Signature{ + uint32 GuardianIndex = 1; + bytes Signature = 2; +} + message SubmitVAAResponse { string signature = 1; } diff --git a/solana/bridge/Cargo.lock b/solana/bridge/Cargo.lock index 5712c0e6e..e02822cc3 100644 --- a/solana/bridge/Cargo.lock +++ b/solana/bridge/Cargo.lock @@ -31,6 +31,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" name = "agent" version = "0.1.0" dependencies = [ + "borsh", "bridge", "bs58", "byteorder", @@ -46,6 +47,7 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", + "sha3", "solana-client", "solana-program", "solana-sdk", diff --git a/solana/bridge/agent/Cargo.toml b/solana/bridge/agent/Cargo.toml index 4684b094f..031ec3e3c 100644 --- a/solana/bridge/agent/Cargo.toml +++ b/solana/bridge/agent/Cargo.toml @@ -14,7 +14,7 @@ solana-program = "=1.7.0" solana-sdk = "=1.7.0" solitaire = { path = "../../solitaire/program" } solitaire-client = {path = "../../solitaire/client" } -bridge = { path = "../program", default-features = false, features = ["no-entrypoint"] } +bridge = { path = "../program", features = ["no-idl", "no-entrypoint", "client"] } primitive-types = { version = "0.7.2" } hex = "0.4.2" thiserror = "1.0.20" @@ -26,10 +26,12 @@ log = "0.4.11" serde_derive = "1.0.103" serde_json = "1.0.57" bs58 = "0.3.1" -byteorder = "1.3.4" +byteorder = "1.4.3" futures = "0.3.8" libc = "0.2.80" clap = "2.33.3" +borsh = "0.8.1" +sha3 = "0.9.1" [build-dependencies] tonic-build = { version = "0.3.0", features = ["prost"] } diff --git a/solana/bridge/agent/build.rs b/solana/bridge/agent/build.rs index 47deb58bc..cf2b77457 100644 --- a/solana/bridge/agent/build.rs +++ b/solana/bridge/agent/build.rs @@ -1,3 +1,3 @@ fn main() { - tonic_build::compile_protos("../../proto/agent/v1/service.proto").unwrap(); + tonic_build::compile_protos("../../../proto/agent/v1/service.proto").unwrap(); } diff --git a/solana/bridge/agent/src/main.rs b/solana/bridge/agent/src/main.rs index 90a96d1f2..bb0a22cfe 100644 --- a/solana/bridge/agent/src/main.rs +++ b/solana/bridge/agent/src/main.rs @@ -1,34 +1,80 @@ -use std::{env, io::Write, mem::size_of, str::FromStr, fs}; -use std::path::Path; use libc; +use std::{ + fs, + io::Write, + path::Path, + str::FromStr, +}; -use clap::{Arg, App, SubCommand}; +use clap::{ + App, + Arg, +}; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{ + LittleEndian, + WriteBytesExt, +}; use futures::stream::TryStreamExt; use solana_client::{ - client_error::ClientError, rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig, + client_error::ClientError, + rpc_client::RpcClient, + rpc_config::RpcSendTransactionConfig, }; use solana_sdk::{ - commitment_config::{CommitmentConfig, CommitmentLevel}, + commitment_config::{ + CommitmentConfig, + CommitmentLevel, + }, instruction::Instruction, pubkey::Pubkey, - signature::{read_keypair_file, write_keypair_file, Keypair, Signature, Signer}, + signature::{ + read_keypair_file, + Keypair, + Signature, + Signer, + }, transaction::Transaction, }; use tokio::net::UnixListener; -use tokio::sync::mpsc; -use tonic::{transport::Server, Code, Request, Response, Status}; -use service::{ - agent_server::{Agent, AgentServer}, - Empty,SubmitVaaRequest, SubmitVaaResponse, - GetBalanceResponse, GetBalanceRequest, +use tonic::{ + transport::Server, + Code, + Request, + Response, + Status, }; -use spl_bridge::{ - instruction::{post_vaa, verify_signatures, VerifySigPayload, CHAIN_ID_SOLANA}, - state::{Bridge, GuardianSet, PostedMessage}, - vaa::VAA, + +use borsh::BorshDeserialize; +use bridge::{ + accounts::{ + GuardianSet, + GuardianSetDerivationData, + }, + instructions::{ + hash_vaa, + post_vaa, + serialize_vaa, + verify_signatures, + }, + types::GuardianSetData, + PostVAAData, + VerifySignaturesData, +}; +use service::{ + agent_server::{ + Agent, + AgentServer, + }, + GetBalanceRequest, + GetBalanceResponse, + SubmitVaaRequest, + SubmitVaaResponse, +}; +use solitaire::{ + processors::seeded::Seeded, + AccountState, }; mod socket; @@ -38,7 +84,6 @@ pub mod service { } pub struct AgentImpl { - url: String, bridge: Pubkey, rpc_url: String, @@ -46,7 +91,7 @@ pub struct AgentImpl { } pub struct SignatureItem { - signature: [u8; 64 + 1], + signature: Vec, key: [u8; 20], index: u8, } @@ -68,43 +113,46 @@ impl Agent for AgentImpl { std::thread::spawn(move || { let rpc = RpcClient::new(rpc_url); - let mut vaa = match VAA::deserialize(&request.get_ref().vaa) { - Ok(v) => v, - Err(e) => { - return Err(Status::new( - Code::InvalidArgument, - format!("could not parse VAA: {}", e), - )); - } + let vaa = &request.get_ref().vaa.as_ref().unwrap(); + + let mut emitter_address = [0u8; 32]; + emitter_address.copy_from_slice(vaa.emitter_address.as_slice()); + let post_data = PostVAAData { + version: vaa.version as u8, + guardian_set_index: vaa.guardian_set_index, + timestamp: vaa.timestamp.as_ref().unwrap().seconds as u32, + nonce: vaa.nonce, + emitter_chain: vaa.emitter_chain as u16, + emitter_address: emitter_address, + sequence: vaa.sequence, + payload: vaa.payload.clone(), }; - let verify_txs = pack_sig_verification_txs(&rpc, &bridge, &vaa, &key)?; + + let verify_txs = + pack_sig_verification_txs(&rpc, &bridge, &post_data, &vaa.signatures, &key)?; // Strip signatures - vaa.signatures = Vec::new(); - let ix = match post_vaa(&bridge, &key.pubkey(), vaa.serialize().unwrap()) { - Ok(v) => v, - Err(e) => { - return Err(Status::new( - Code::InvalidArgument, - format!("could not create post_vaa instruction: {}", e), - )); - } - }; + let ix = post_vaa(bridge, key.pubkey(), post_data); for mut tx in verify_txs { - match sign_and_send(&rpc, &mut tx, vec![&key], request.skip_preflight) { + match sign_and_send(&rpc, &mut tx, vec![&key], request.get_ref().skip_preflight) { Ok(_) => (), Err(e) => { return Err(Status::new( Code::Internal, - format!("tx sending failed: {}", e), + format!("tx sending failed: {:?}", e), )); } }; } let mut transaction2 = Transaction::new_with_payer(&[ix], Some(&key.pubkey())); - match sign_and_send(&rpc, &mut transaction2, vec![&key], request.skip_preflight) { + match sign_and_send( + &rpc, + &mut transaction2, + vec![&key], + request.into_inner().skip_preflight, + ) { Ok(s) => Ok(Response::new(SubmitVaaResponse { signature: s.to_string(), })), @@ -114,13 +162,13 @@ impl Agent for AgentImpl { )), } }) - .join() - .unwrap() + .join() + .unwrap() } async fn get_balance( &self, - request: Request, + _request: Request, ) -> Result, Status> { // Hack to clone keypair let b = self.key.pubkey(); @@ -136,30 +184,32 @@ impl Agent for AgentImpl { Err(e) => { return Err(Status::new( Code::Internal, - format!("failed to fetch balance: {}", e), + format!("failed to fetch balance: {:?}", e), )); } }; - Ok(Response::new(GetBalanceResponse { - balance, - })) + Ok(Response::new(GetBalanceResponse { balance })) }) - .join() - .unwrap() + .join() + .unwrap() } } fn pack_sig_verification_txs<'a>( rpc: &RpcClient, bridge: &Pubkey, - vaa: &VAA, + vaa: &PostVAAData, + signatures: &Vec, sender_keypair: &'a Keypair, ) -> Result, Status> { // Load guardian set - let bridge_key = Bridge::derive_bridge_id(bridge).unwrap(); - let guardian_key = - Bridge::derive_guardian_set_id(bridge, &bridge_key, vaa.guardian_set_index).unwrap(); + let guardian_key = GuardianSet::<'_, { AccountState::Initialized }>::key( + &GuardianSetDerivationData { + index: vaa.guardian_set_index, + }, + bridge, + ); let guardian_account = rpc .get_account_with_commitment( &guardian_key, @@ -171,78 +221,54 @@ fn pack_sig_verification_txs<'a>( .value .unwrap_or_default(); let data = guardian_account.data; - let guardian_set: &GuardianSet = Bridge::unpack_immutable(data.as_slice()).unwrap(); + let guardian_set: GuardianSetData = GuardianSetData::try_from_slice(data.as_slice()).unwrap(); // Map signatures to guardian set let mut signature_items: Vec = Vec::new(); - for s in vaa.signatures.iter() { + for s in signatures.iter() { let mut item = SignatureItem { - signature: [0; 64 + 1], + signature: s.signature.clone(), key: [0; 20], - index: s.index, + index: s.guardian_index as u8, }; - - item.signature[0..32].copy_from_slice(&s.r); - item.signature[32..64].copy_from_slice(&s.s); - item.signature[64] = s.v; - item.key = guardian_set.keys[s.index as usize]; + item.key = guardian_set.keys[s.guardian_index as usize]; signature_items.push(item); } - let vaa_hash = match vaa.body_hash() { - Ok(v) => v, - Err(e) => { - return Err(Status::new( - Code::InvalidArgument, - format!("could get vaa body hash: {}", e), - )); - } - }; - let vaa_body = match vaa.signature_body() { - Ok(v) => v, - Err(e) => { - return Err(Status::new( - Code::InvalidArgument, - format!("could get vaa body: {}", e), - )); - } - }; - - let signature_acc = - Bridge::derive_signature_id(&bridge, &bridge_key, &vaa_hash, guardian_set.index).unwrap(); + let vaa_body = serialize_vaa(vaa); let mut verify_txs: Vec = Vec::new(); - for (tx_index, chunk) in signature_items.chunks(6).enumerate() { + for (_tx_index, chunk) in signature_items.chunks(6).enumerate() { let mut secp_payload = Vec::new(); - let mut signature_status = [-1i8; 20]; + let mut signature_status = [-1i8; 19]; let data_offset = 1 + chunk.len() * 11; let message_offset = data_offset + chunk.len() * 85; // 1 number of signatures - secp_payload.write_u8(chunk.len() as u8); + secp_payload.write_u8(chunk.len() as u8)?; // Secp signature info description (11 bytes * n) for (i, s) in chunk.iter().enumerate() { - secp_payload.write_u16::((data_offset + 85 * i) as u16); - secp_payload.write_u8(0); - secp_payload.write_u16::((data_offset + 85 * i + 65) as u16); - secp_payload.write_u8(0); - secp_payload.write_u16::(message_offset as u16); - secp_payload.write_u16::(vaa_body.len() as u16); - secp_payload.write_u8(0); + secp_payload.write_u16::((data_offset + 85 * i) as u16)?; + secp_payload.write_u8(0)?; + secp_payload.write_u16::((data_offset + 85 * i + 65) as u16)?; + secp_payload.write_u8(0)?; + secp_payload.write_u16::(message_offset as u16)?; + secp_payload.write_u16::(vaa_body.len() as u16)?; + secp_payload.write_u8(0)?; signature_status[s.index as usize] = i as i8; } // Write signatures and addresses for s in chunk.iter() { - secp_payload.write(&s.signature); - secp_payload.write(&s.key); + secp_payload.write(&s.signature)?; + secp_payload.write(&s.key)?; } // Write body - secp_payload.write(&vaa_body); + secp_payload.write(&vaa_body)?; let secp_ix = Instruction { program_id: solana_sdk::secp256k1_program::id(), @@ -250,24 +276,25 @@ fn pack_sig_verification_txs<'a>( accounts: vec![], }; - let payload = VerifySigPayload { + let body_hash: [u8; 32] = hash_vaa(vaa); + + let payload = VerifySignaturesData { signers: signature_status, - hash: vaa_hash, + hash: body_hash, initial_creation: false, }; let verify_ix = match verify_signatures( - &bridge, - &signature_acc, - &sender_keypair.pubkey(), + *bridge, + sender_keypair.pubkey(), vaa.guardian_set_index, - &payload, + payload, ) { Ok(v) => v, Err(e) => { return Err(Status::new( Code::InvalidArgument, - format!("could not create verify instruction: {}", e), + format!("could not create verify instruction: {:?}", e), )); } }; @@ -307,40 +334,49 @@ fn sign_and_send( #[tokio::main] async fn main() -> Result<(), Box> { let matches = App::new("Wormhole Solana agent") - .arg(Arg::with_name("bridge") - .long("bridge") - .value_name("ADDRESS") - .help("Bridge address") - .required(true) - .takes_value(true)) - .arg(Arg::with_name("ws") - .long("ws") - .value_name("URI") - .help("PubSub Websocket URI (ws[s]://)") - .required(true) - .takes_value(true)) - .arg(Arg::with_name("rpc") - .long("rpc") - .value_name("URI") - .help("RPC URI (http[s]://)") - .required(true) - .takes_value(true)) - .arg(Arg::with_name("socket") - .long("socket") - .value_name("FILE") - .help("Path to agent socket") - .required(true) - .takes_value(true)) - .arg(Arg::with_name("keypair") - .long("keypair") - .value_name("FILE") - .help("Fee payer account key ") - .required(true) - .takes_value(true)) + .arg( + Arg::with_name("bridge") + .long("bridge") + .value_name("ADDRESS") + .help("Bridge address") + .required(true) + .takes_value(true), + ) + .arg( + Arg::with_name("ws") + .long("ws") + .value_name("URI") + .help("PubSub Websocket URI (ws[s]://)") + .required(true) + .takes_value(true), + ) + .arg( + Arg::with_name("rpc") + .long("rpc") + .value_name("URI") + .help("RPC URI (http[s]://)") + .required(true) + .takes_value(true), + ) + .arg( + Arg::with_name("socket") + .long("socket") + .value_name("FILE") + .help("Path to agent socket") + .required(true) + .takes_value(true), + ) + .arg( + Arg::with_name("keypair") + .long("keypair") + .value_name("FILE") + .help("Fee payer account key ") + .required(true) + .takes_value(true), + ) .get_matches(); let bridge = matches.value_of("bridge").unwrap(); - let ws_url = matches.value_of("ws").unwrap(); let rpc_url = matches.value_of("rpc").unwrap(); let socket_path = matches.value_of("socket").unwrap(); let keypair = read_keypair_file(matches.value_of("keypair").unwrap()).unwrap(); @@ -348,7 +384,6 @@ async fn main() -> Result<(), Box> { println!("Agent using account: {}", keypair.pubkey()); let agent = AgentImpl { - url: ws_url.to_string(), rpc_url: rpc_url.to_string(), bridge: Pubkey::from_str(bridge).unwrap(), key: keypair, @@ -359,7 +394,7 @@ async fn main() -> Result<(), Box> { unsafe { libc::umask(0o0077) }; // Delete existing socket file and recreate it with restrictive permissions. - let mut path = Path::new(socket_path); + let path = Path::new(socket_path); if path.exists() { fs::remove_file(path)?; } diff --git a/solana/bridge/program/src/api/post_vaa.rs b/solana/bridge/program/src/api/post_vaa.rs index 275636dc2..ec3eeeffb 100644 --- a/solana/bridge/program/src/api/post_vaa.rs +++ b/solana/bridge/program/src/api/post_vaa.rs @@ -100,7 +100,6 @@ pub struct PostVAAData { // Header part pub version: u8, pub guardian_set_index: u32, - pub signatures: Vec, // Body part pub timestamp: u32, diff --git a/solana/bridge/program/src/instructions.rs b/solana/bridge/program/src/instructions.rs index 717b74335..f52752eb3 100644 --- a/solana/bridge/program/src/instructions.rs +++ b/solana/bridge/program/src/instructions.rs @@ -1,18 +1,10 @@ use borsh::BorshSerialize; use solana_program::{ - borsh::try_from_slice_unchecked, - hash, instruction::{ AccountMeta, Instruction, }, - program_pack::Pack, pubkey::Pubkey, - system_instruction::{ - self, - create_account, - }, - system_program, sysvar, }; @@ -34,7 +26,6 @@ use crate::{ SignatureSet, SignatureSetDerivationData, }, - types::PostedMessage, BridgeConfig, PostMessageData, PostVAAData, @@ -124,7 +115,7 @@ pub fn verify_signatures( program_id: Pubkey, payer: Pubkey, guardian_set_index: u32, - hash: [u8; 32], + data: VerifySignaturesData, ) -> solitaire::Result { let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key( &GuardianSetDerivationData { @@ -134,15 +125,10 @@ pub fn verify_signatures( ); let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key( - &SignatureSetDerivationData { hash }, + &SignatureSetDerivationData { hash: data.hash }, &program_id, ); - // Bridge with a single pre-existing signer. - // TODO: Get rid of this, exists to make testing easier for now. - let mut signers = [-1; 19]; - signers[0] = 0; - Ok(Instruction { program_id, @@ -155,40 +141,31 @@ pub fn verify_signatures( AccountMeta::new_readonly(solana_program::system_program::id(), false), ], - data: crate::instruction::Instruction::VerifySignatures(VerifySignaturesData { - hash, - signers, - initial_creation: true, - }) - .try_to_vec()?, + data: crate::instruction::Instruction::VerifySignatures(data).try_to_vec()?, }) } -pub fn post_vaa( - program_id: Pubkey, - payer: Pubkey, - emitter: Pubkey, - guardian_set_index: u32, - vaa: PostVAAData, -) -> Instruction { +pub fn post_vaa(program_id: Pubkey, payer: Pubkey, vaa: PostVAAData) -> Instruction { let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id); let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key( &GuardianSetDerivationData { - index: guardian_set_index, + index: vaa.guardian_set_index, }, &program_id, ); let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key( - &SignatureSetDerivationData { hash: hash_vaa(&vaa) }, + &SignatureSetDerivationData { + hash: hash_vaa(&vaa), + }, &program_id, ); let message = Message::<'_, { AccountState::MaybeInitialized }>::key( &MessageDerivationData { - emitter_key: emitter.to_bytes(), - emitter_chain: 1, - nonce: 0, + emitter_key: vaa.emitter_address, + emitter_chain: vaa.emitter_chain, + nonce: vaa.nonce, payload: vaa.payload.clone(), }, &program_id, @@ -216,7 +193,7 @@ pub fn post_vaa( // Convert a full VAA structure into the serialization of its unique components, this structure is // what is hashed and verified by Guardians. -fn serialize_vaa(vaa: &PostVAAData) -> Vec { +pub fn serialize_vaa(vaa: &PostVAAData) -> Vec { use byteorder::{ BigEndian, WriteBytesExt, @@ -231,12 +208,13 @@ fn serialize_vaa(vaa: &PostVAAData) -> Vec { v.write_u32::(vaa.nonce).unwrap(); v.write_u16::(vaa.emitter_chain).unwrap(); v.write(&vaa.emitter_address).unwrap(); + v.write_u64::(vaa.sequence).unwrap(); v.write(&vaa.payload).unwrap(); v.into_inner() } // Hash a VAA, this combines serialization and hashing. -fn hash_vaa(vaa: &PostVAAData) -> [u8; 32] { +pub fn hash_vaa(vaa: &PostVAAData) -> [u8; 32] { use sha3::Digest; use std::io::Write; diff --git a/solana/bridge/program/src/lib.rs b/solana/bridge/program/src/lib.rs index dba6b7764..c56297a27 100644 --- a/solana/bridge/program/src/lib.rs +++ b/solana/bridge/program/src/lib.rs @@ -1,5 +1,6 @@ #![feature(const_generics)] #![allow(non_upper_case_globals)] +#![allow(incomplete_features)] use solana_program::msg; diff --git a/solana/bridge/program/tests/common.rs b/solana/bridge/program/tests/common.rs index 56bcfcb2a..50033af95 100644 --- a/solana/bridge/program/tests/common.rs +++ b/solana/bridge/program/tests/common.rs @@ -152,7 +152,7 @@ mod helpers { 0, data, ) - .unwrap()], + .unwrap()], ); } @@ -164,13 +164,20 @@ mod helpers { body_hash: [u8; 32], secret_key: SecretKey, ) { + let mut signers = [-1; 19]; + signers[0] = 0; + execute( client, payer, &[payer], &[ new_secp256k1_instruction(&secret_key, &body), - instructions::verify_signatures(*program, payer.pubkey(), 0, body_hash).unwrap(), + instructions::verify_signatures(*program, payer.pubkey(), 0, VerifySignaturesData { + hash: body_hash, + signers, + initial_creation: true, + }).unwrap(), ], ); } @@ -179,7 +186,6 @@ mod helpers { client: &RpcClient, program: &Pubkey, payer: &Keypair, - emitter: &Pubkey, vaa: PostVAAData, ) { execute( @@ -189,8 +195,6 @@ mod helpers { &[instructions::post_vaa( *program, payer.pubkey(), - *emitter, - 0, vaa, )], ); diff --git a/solana/bridge/program/tests/integration.rs b/solana/bridge/program/tests/integration.rs index 064f79ba0..204de3a99 100644 --- a/solana/bridge/program/tests/integration.rs +++ b/solana/bridge/program/tests/integration.rs @@ -33,7 +33,7 @@ use byteorder::{ BigEndian, WriteBytesExt, }; - + use std::convert::TryInto; use std::io::{ Cursor, @@ -112,7 +112,6 @@ fn test_bridge_messages() { client, program, payer, - &emitter.pubkey(), vaa, ); @@ -123,12 +122,11 @@ fn test_bridge_messages() { /// is on the chain and creating a signature set for it. fn guardian_sign_round( emitter: &Keypair, - data: Vec + data: Vec, ) -> (PostVAAData, Vec, [u8; 32], secp256k1::SecretKey) { let mut vaa = PostVAAData { version: 0, guardian_set_index: 0, - signatures: vec![], // Body part nonce: 0, @@ -155,9 +153,9 @@ fn guardian_sign_round( // Public Key: 0x1d72877eb2d898738afe94c6101152ede0435de9 let secret_key = secp256k1::SecretKey::parse(&[ - 0x99, 0x70, 0x1c, 0x80, 0x5e, 0xf9, 0x38, 0xe1, 0x3f, 0x0e, 0x48, 0xf0, 0x9e, 0x2c, 0x32, - 0x78, 0x91, 0xc1, 0xd8, 0x47, 0x29, 0xd1, 0x52, 0xf3, 0x01, 0xe7, 0xe6, 0x2c, 0xbf, 0x1f, - 0x91, 0xc9 + 0x99, 0x70, 0x1c, 0x80, 0x5e, 0xf9, 0x38, 0xe1, 0x3f, 0x0e, 0x48, 0xf0, 0x9e, 0x2c, 0x32, + 0x78, 0x91, 0xc1, 0xd8, 0x47, 0x29, 0xd1, 0x52, 0xf3, 0x01, 0xe7, 0xe6, 0x2c, 0xbf, 0x1f, + 0x91, 0xc9 ]).unwrap(); let public_key = secp256k1::PublicKey::from_secret_key(&secret_key); @@ -181,9 +179,9 @@ fn guardian_sign_round( let signature = sig.0.serialize(); vaa.signatures.push(Signature { index: 0, - r: signature[0..32].try_into().unwrap(), - s: signature[32..64].try_into().unwrap(), - v: sig.1.serialize(), + r: signature[0..32].try_into().unwrap(), + s: signature[32..64].try_into().unwrap(), + v: sig.1.serialize(), }); (vaa, body, body_hash, secret_key)