11use {
2- crate :: config:: Command ,
2+ crate :: {
3+ config:: Command ,
4+ signer:: { GuardianKey , Signer , GUARDIAN_KEY_ARMORED_BLOCK , STANDARD_ARMOR_LINE_HEADER } ,
5+ } ,
36 api_client:: { ApiClient , Observation } ,
47 borsh:: BorshDeserialize ,
58 clap:: Parser ,
69 posted_message:: PostedMessageUnreliableData ,
710 prost:: Message ,
8- secp256k1:: { rand:: rngs:: OsRng , PublicKey , Secp256k1 , SecretKey } ,
9- sequoia_openpgp:: armor:: { Kind , Reader , ReaderMode , Writer } ,
11+ secp256k1:: { rand:: rngs:: OsRng , Secp256k1 } ,
12+ sequoia_openpgp:: armor:: { Kind , Writer } ,
1013 serde_wormhole:: RawMessage ,
11- sha3:: { Digest , Keccak256 } ,
1214 solana_account_decoder:: UiAccountEncoding ,
1315 solana_client:: {
1416 nonblocking:: pubsub_client:: PubsubClient ,
2022 solana_sdk:: pubkey:: Pubkey ,
2123 std:: {
2224 fs,
23- io:: { Cursor , IsTerminal , Read , Write } ,
25+ io:: { IsTerminal , Write } ,
2426 str:: FromStr ,
2527 time:: Duration ,
2628 } ,
@@ -32,10 +34,11 @@ use {
3234mod api_client;
3335mod config;
3436mod posted_message;
37+ mod signer;
3538
36- struct RunListenerInput {
39+ struct RunListenerInput < T : Signer > {
3740 ws_url : String ,
38- secret_key : SecretKey ,
41+ signer : T ,
3942 wormhole_pid : Pubkey ,
4043 accumulator_address : Pubkey ,
4144 api_client : ApiClient ,
@@ -112,7 +115,9 @@ fn message_data_to_body(unreliable_data: &PostedMessageUnreliableData) -> Body<&
112115 }
113116}
114117
115- async fn run_listener ( input : RunListenerInput ) -> Result < ( ) , PubsubClientError > {
118+ async fn run_listener < T : Signer + ' static > (
119+ input : RunListenerInput < T > ,
120+ ) -> Result < ( ) , PubsubClientError > {
116121 let client = PubsubClient :: new ( input. ws_url . as_str ( ) ) . await ?;
117122 let ( mut stream, unsubscribe) = client
118123 . program_subscribe (
@@ -143,10 +148,10 @@ async fn run_listener(input: RunListenerInput) -> Result<(), PubsubClientError>
143148 } ;
144149
145150 tokio:: spawn ( {
146- let api_client = input. api_client . clone ( ) ;
151+ let ( api_client, signer ) = ( input. api_client . clone ( ) , input . signer . clone ( ) ) ;
147152 async move {
148153 let body = message_data_to_body ( & unreliable_data) ;
149- match Observation :: try_new ( body. clone ( ) , input . secret_key ) {
154+ match Observation :: try_new ( body. clone ( ) , signer . clone ( ) ) {
150155 Ok ( observation) => {
151156 if let Err ( e) = api_client. post_observation ( observation) . await {
152157 tracing:: error!( error = ?e, "Failed to post observation" ) ;
@@ -167,60 +172,8 @@ async fn run_listener(input: RunListenerInput) -> Result<(), PubsubClientError>
167172 ) )
168173}
169174
170- #[ derive( Clone , PartialEq , Message ) ]
171- pub struct GuardianKey {
172- #[ prost( bytes = "vec" , tag = "1" ) ]
173- pub data : Vec < u8 > ,
174- #[ prost( bool , tag = "2" ) ]
175- pub unsafe_deterministic_key : bool ,
176- }
177-
178- const GUARDIAN_KEY_ARMORED_BLOCK : & str = "WORMHOLE GUARDIAN PRIVATE KEY" ;
179- const STANDARD_ARMOR_LINE_HEADER : & str = "PGP PRIVATE KEY BLOCK" ;
180-
181- fn parse_and_verify_proto_guardian_key ( content : String , mode : crate :: config:: Mode ) -> GuardianKey {
182- let content = content. replace ( GUARDIAN_KEY_ARMORED_BLOCK , STANDARD_ARMOR_LINE_HEADER ) ;
183- let cursor = Cursor :: new ( content) ;
184- let mut armor_reader = Reader :: from_reader ( cursor, ReaderMode :: Tolerant ( Some ( Kind :: SecretKey ) ) ) ;
185-
186- let mut buf = Vec :: new ( ) ;
187- armor_reader
188- . read_to_end ( & mut buf)
189- . expect ( "Failed to read armored content" ) ;
190-
191- let guardian_key =
192- GuardianKey :: decode ( & mut buf. as_slice ( ) ) . expect ( "Failed to decode GuardianKey" ) ;
193-
194- if let crate :: config:: Mode :: Production = mode {
195- if guardian_key. unsafe_deterministic_key {
196- panic ! ( "Unsafe deterministic key is not allowed in production mode" ) ;
197- }
198- }
199-
200- guardian_key
201- }
202-
203- fn load_secret_key ( run_options : config:: RunOptions ) -> SecretKey {
204- let content = fs:: read_to_string ( run_options. secret_key_path ) . expect ( "Failed to read file" ) ;
205- let guardian_key = parse_and_verify_proto_guardian_key ( content, run_options. mode ) ;
206- SecretKey :: from_slice ( & guardian_key. data ) . expect ( "Failed to create SecretKey from bytes" )
207- }
208-
209- fn get_public_key ( secret_key : & SecretKey ) -> ( PublicKey , [ u8 ; 20 ] ) {
210- let secp = Secp256k1 :: new ( ) ;
211- let public_key = secret_key. public_key ( & secp) ;
212- let pubkey_uncompressed = public_key. serialize_uncompressed ( ) ;
213- let pubkey_hash: [ u8 ; 32 ] = Keccak256 :: new_with_prefix ( & pubkey_uncompressed[ 1 ..] )
214- . finalize ( )
215- . into ( ) ;
216- let pubkey_evm: [ u8 ; 20 ] = pubkey_hash[ pubkey_hash. len ( ) - 20 ..]
217- . try_into ( )
218- . expect ( "Invalid address length" ) ;
219- ( public_key, pubkey_evm)
220- }
221-
222175async fn run ( run_options : config:: RunOptions ) {
223- let secret_key = load_secret_key ( run_options. clone ( ) ) ;
176+ let signer = signer :: FileSigner :: try_new ( run_options. clone ( ) ) . expect ( "Failed to create signer" ) ;
224177 let client = PubsubClient :: new ( & run_options. pythnet_url )
225178 . await
226179 . expect ( "Invalid WebSocket URL" ) ;
@@ -232,7 +185,7 @@ async fn run(run_options: config::RunOptions) {
232185 let api_client =
233186 ApiClient :: try_new ( run_options. server_url , None ) . expect ( "Failed to create API client" ) ;
234187
235- let ( pubkey, pubkey_evm) = get_public_key ( & secret_key ) ;
188+ let ( pubkey, pubkey_evm) = signer . get_public_key ( ) . expect ( "Failed to get public key" ) ;
236189 let evm_encded_public_key = format ! ( "0x{}" , hex:: encode( pubkey_evm) ) ;
237190 tracing:: info!(
238191 public_key = ?pubkey,
@@ -243,7 +196,7 @@ async fn run(run_options: config::RunOptions) {
243196 loop {
244197 if let Err ( e) = run_listener ( RunListenerInput {
245198 ws_url : run_options. pythnet_url . clone ( ) ,
246- secret_key ,
199+ signer : signer . clone ( ) ,
247200 wormhole_pid,
248201 accumulator_address,
249202 api_client : api_client. clone ( ) ,
@@ -285,7 +238,8 @@ async fn main() {
285238
286239 // Generate keypair (secret + public key)
287240 let ( secret_key, _) = secp. generate_keypair ( & mut rng) ;
288- let ( pubkey, pubkey_evm) = get_public_key ( & secret_key) ;
241+ let signer = signer:: FileSigner { secret_key } ;
242+ let ( pubkey, pubkey_evm) = signer. get_public_key ( ) . expect ( "Failed to get public key" ) ;
289243
290244 let guardian_key = GuardianKey {
291245 data : secret_key. secret_bytes ( ) . to_vec ( ) ,
@@ -322,6 +276,7 @@ mod tests {
322276
323277 use base64:: Engine ;
324278 use borsh:: BorshSerialize ;
279+ use secp256k1:: SecretKey ;
325280 use solana_account_decoder:: { UiAccount , UiAccountData } ;
326281
327282 use crate :: posted_message:: MessageData ;
@@ -516,16 +471,21 @@ mod tests {
516471 -----END WORMHOLE GUARDIAN PRIVATE KEY-----
517472 "
518473 . to_string ( ) ;
519- let guardian_key = parse_and_verify_proto_guardian_key ( content, config:: Mode :: Production ) ;
474+ let guardian_key = crate :: signer:: FileSigner :: parse_and_verify_proto_guardian_key (
475+ content,
476+ config:: Mode :: Production ,
477+ )
478+ . expect ( "Failed to parse and verify guardian key" ) ;
520479 assert ! ( !guardian_key. unsafe_deterministic_key) ;
521480 let secret_key = SecretKey :: from_slice ( & guardian_key. data )
522481 . expect ( "Failed to create SecretKey from bytes" ) ;
482+ let signer = signer:: FileSigner { secret_key } ;
523483 assert_eq ! (
524484 hex:: encode( secret_key. secret_bytes( ) ) ,
525485 "f2f3127bff540c8441f99763f586858ef340c9962ad62b6181cd77203e81808f" ,
526486 ) ;
527487 assert_eq ! (
528- hex:: encode( get_public_key( & secret_key ) . 1 ) ,
488+ hex:: encode( signer . get_public_key( ) . expect ( "Failed to get public key" ) . 1 ) ,
529489 "30e41be3f10d3ac813f91e49e189bbb948d030be" ,
530490 ) ;
531491 }
0 commit comments