11use borsh:: BorshDeserialize ;
22use observation:: { Body , SignedBody } ;
33use posted_message:: PostedMessageUnreliableData ;
4+ use secp256k1:: { ecdsa:: { RecoverableSignature , RecoveryId } , Message , PublicKey , Secp256k1 , SecretKey } ;
5+ use serde_wormhole:: RawMessage ;
6+ use sha3:: digest:: crypto_common:: rand_core:: OsRng ;
47use solana_account_decoder:: UiAccountEncoding ;
58use solana_client:: {
69 nonblocking:: pubsub_client:: PubsubClient ,
710 rpc_config:: { RpcAccountInfoConfig , RpcProgramAccountsConfig } ,
811} ;
9- use solana_sdk:: { pubkey:: Pubkey , signature:: Keypair } ;
12+ use solana_sdk:: pubkey:: Pubkey ;
13+ use wormhole_sdk:: { vaa:: { Body as BodyWormhole , Header , Signature } , Address , Chain , Vaa } ;
1014use std:: str:: FromStr ;
1115use tokio_stream:: StreamExt ;
16+ use sha3:: { Digest , Keccak256 } ;
17+ use sha3:: digest:: crypto_common:: rand_core:: RngCore ;
1218
1319mod posted_message;
1420mod observation;
1521mod serde_array;
1622
1723const PYTHNET_CHAIN_ID : u16 = 26 ;
1824
25+ fn generate_guardian_key ( ) -> ( SecretKey , [ u8 ; 20 ] ) {
26+ let secp = Secp256k1 :: new ( ) ;
27+ let mut rng = OsRng ;
28+
29+ let mut sk_bytes = [ 0u8 ; 32 ] ;
30+ rng. fill_bytes ( & mut sk_bytes) ;
31+ let secret_key = SecretKey :: from_slice ( & sk_bytes) . expect ( "Failed to create secret key" ) ;
32+
33+ let public_key = PublicKey :: from_secret_key ( & secp, & secret_key) ;
34+ let uncompressed = public_key. serialize_uncompressed ( ) ;
35+
36+ let hash = Keccak256 :: digest ( & uncompressed[ 1 ..] ) ;
37+ let address: [ u8 ; 20 ] = hash[ 12 ..] . try_into ( ) . unwrap ( ) ;
38+
39+ ( secret_key, address)
40+ }
41+
42+ pub fn verify_vaa ( public_key_original : [ u8 ; 20 ] , signed_body : SignedBody < Vec < u8 > > ) -> bool {
43+ let mut signature = Signature :: default ( ) ;
44+ signature. signature = signed_body. signature ;
45+ let vaa: Vaa < Vec < u8 > > = Vaa {
46+ version : signed_body. version ,
47+ guardian_set_index : signed_body. guardian_set_index ,
48+ signatures : vec ! [ signature] ,
49+ timestamp : signed_body. body . timestamp ,
50+ nonce : signed_body. body . nonce ,
51+ emitter_chain : Chain :: Pythnet ,
52+ emitter_address : Address ( signed_body. body . emitter_address ) ,
53+ sequence : signed_body. body . sequence ,
54+ consistency_level : 1 ,
55+ payload : signed_body. body . payload ,
56+ } ;
57+ let ( _, body) : ( Header , BodyWormhole < Vec < u8 > > ) = vaa. into ( ) ;
58+ let digest = body. digest ( ) . expect ( "Failed to get digest" ) ;
59+
60+ let secp = Secp256k1 :: new ( ) ;
61+ let signature: [ u8 ; 65 ] = signed_body. signature ;
62+
63+ // Recover the public key from an [u8; 65] serialized ECDSA signature in (v, r, s) format
64+ let recid = RecoveryId :: try_from ( signature[ 64 ] as i32 ) . expect ( "Failed to create recovery ID" ) ;
65+
66+ // An address is the last 20 bytes of the Keccak256 hash of the uncompressed public key.
67+ let pubkey: & [ u8 ; 65 ] = & secp
68+ . recover_ecdsa (
69+ Message :: from_digest ( digest. secp256k_hash ) ,
70+ & RecoverableSignature :: from_compact ( & signature[ ..64 ] , recid) . expect ( "Failed to create recoverable signature" ) ,
71+ )
72+ . expect ( "Failed to recover public key" )
73+ . serialize_uncompressed ( ) ;
74+
75+ // The address is the last 20 bytes of the Keccak256 hash of the public key
76+ let address: [ u8 ; 32 ] = Keccak256 :: new_with_prefix ( & pubkey[ 1 ..] ) . finalize ( ) . into ( ) ;
77+ let address: [ u8 ; 20 ] = address[ address. len ( ) - 20 ..] . try_into ( )
78+ . expect ( "Failed to convert address to 20 bytes" ) ;
79+
80+ println ! ( "Recovered address: {:?}" , address) ;
81+ println ! ( "Public key: {:?}" , public_key_original) ;
82+ // Confirm the recovered address matches an address in the guardian set.
83+ public_key_original == address
84+ }
85+
1986#[ tokio:: main]
2087async fn main ( ) {
2188 let ws_url = "wss://api2.pythnet.pyth.network/" ;
2289 let accumulator_address = Pubkey :: from_str ( "G9LV2mp9ua1znRAfYwZz5cPiJMAbo1T6mbjdQsDZuMJg" ) . unwrap ( ) ; // Replace with actual Wormhole address
2390 let wormhole_pid = Pubkey :: from_str ( "H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU" ) . unwrap ( ) ; // Replace with actual Wormhole program ID
24- let secret_key = Keypair :: new ( ) . secret ( ) . to_bytes ( ) ;
91+ let ( secret_key, pulick_key) = generate_guardian_key ( ) ;
92+ println ! ( "Generated secret key: {:?}" , secret_key) ;
93+ println ! ( "Generated public key: {:?}" , pulick_key) ;
2594 let index = 0 ;
2695
2796 let client = PubsubClient :: new ( ws_url) . await . unwrap ( ) ;
@@ -73,7 +142,7 @@ async fn main() {
73142 consistency_level : unreliable_data. consistency_level ,
74143 payload : unreliable_data. payload . clone ( ) ,
75144 } ;
76- match body. sign ( secret_key) {
145+ match body. sign ( secret_key. secret_bytes ( ) ) {
77146 Ok ( signature) => {
78147 let signed_body = SignedBody {
79148 version : unreliable_data. vaa_version ,
@@ -83,6 +152,7 @@ async fn main() {
83152 } ;
84153 // Post it to server
85154 println ! ( "message: {:?}" , signed_body) ;
155+ println ! ( "The message is verified {}" , verify_vaa( pulick_key, signed_body) ) ;
86156 }
87157 Err ( e) => tracing:: error!( error = ?e, "Failed to sign body" ) ,
88158 }
0 commit comments