1
1
use {
2
- crate :: config:: Command ,
2
+ crate :: {
3
+ config:: Command ,
4
+ signer:: { GuardianKey , Signer , GUARDIAN_KEY_ARMORED_BLOCK , STANDARD_ARMOR_LINE_HEADER } ,
5
+ } ,
3
6
api_client:: { ApiClient , Observation } ,
4
7
borsh:: BorshDeserialize ,
5
8
clap:: Parser ,
6
9
posted_message:: PostedMessageUnreliableData ,
7
10
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 } ,
10
13
serde_wormhole:: RawMessage ,
11
- sha3:: { Digest , Keccak256 } ,
12
14
solana_account_decoder:: UiAccountEncoding ,
13
15
solana_client:: {
14
16
nonblocking:: pubsub_client:: PubsubClient ,
20
22
solana_sdk:: pubkey:: Pubkey ,
21
23
std:: {
22
24
fs,
23
- io:: { Cursor , IsTerminal , Read , Write } ,
25
+ io:: { IsTerminal , Write } ,
24
26
str:: FromStr ,
25
27
time:: Duration ,
26
28
} ,
@@ -32,10 +34,11 @@ use {
32
34
mod api_client;
33
35
mod config;
34
36
mod posted_message;
37
+ mod signer;
35
38
36
- struct RunListenerInput {
39
+ struct RunListenerInput < T : Signer > {
37
40
ws_url : String ,
38
- secret_key : SecretKey ,
41
+ signer : T ,
39
42
wormhole_pid : Pubkey ,
40
43
accumulator_address : Pubkey ,
41
44
api_client : ApiClient ,
@@ -112,7 +115,9 @@ fn message_data_to_body(unreliable_data: &PostedMessageUnreliableData) -> Body<&
112
115
}
113
116
}
114
117
115
- async fn run_listener ( input : RunListenerInput ) -> Result < ( ) , PubsubClientError > {
118
+ async fn run_listener < T : Signer + ' static > (
119
+ input : RunListenerInput < T > ,
120
+ ) -> Result < ( ) , PubsubClientError > {
116
121
let client = PubsubClient :: new ( input. ws_url . as_str ( ) ) . await ?;
117
122
let ( mut stream, unsubscribe) = client
118
123
. program_subscribe (
@@ -143,10 +148,10 @@ async fn run_listener(input: RunListenerInput) -> Result<(), PubsubClientError>
143
148
} ;
144
149
145
150
tokio:: spawn ( {
146
- let api_client = input. api_client . clone ( ) ;
151
+ let ( api_client, signer ) = ( input. api_client . clone ( ) , input . signer . clone ( ) ) ;
147
152
async move {
148
153
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 ( ) ) {
150
155
Ok ( observation) => {
151
156
if let Err ( e) = api_client. post_observation ( observation) . await {
152
157
tracing:: error!( error = ?e, "Failed to post observation" ) ;
@@ -167,60 +172,8 @@ async fn run_listener(input: RunListenerInput) -> Result<(), PubsubClientError>
167
172
) )
168
173
}
169
174
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
-
222
175
async 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" ) ;
224
177
let client = PubsubClient :: new ( & run_options. pythnet_url )
225
178
. await
226
179
. expect ( "Invalid WebSocket URL" ) ;
@@ -232,7 +185,7 @@ async fn run(run_options: config::RunOptions) {
232
185
let api_client =
233
186
ApiClient :: try_new ( run_options. server_url , None ) . expect ( "Failed to create API client" ) ;
234
187
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" ) ;
236
189
let evm_encded_public_key = format ! ( "0x{}" , hex:: encode( pubkey_evm) ) ;
237
190
tracing:: info!(
238
191
public_key = ?pubkey,
@@ -243,7 +196,7 @@ async fn run(run_options: config::RunOptions) {
243
196
loop {
244
197
if let Err ( e) = run_listener ( RunListenerInput {
245
198
ws_url : run_options. pythnet_url . clone ( ) ,
246
- secret_key ,
199
+ signer : signer . clone ( ) ,
247
200
wormhole_pid,
248
201
accumulator_address,
249
202
api_client : api_client. clone ( ) ,
@@ -285,7 +238,8 @@ async fn main() {
285
238
286
239
// Generate keypair (secret + public key)
287
240
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" ) ;
289
243
290
244
let guardian_key = GuardianKey {
291
245
data : secret_key. secret_bytes ( ) . to_vec ( ) ,
@@ -322,6 +276,7 @@ mod tests {
322
276
323
277
use base64:: Engine ;
324
278
use borsh:: BorshSerialize ;
279
+ use secp256k1:: SecretKey ;
325
280
use solana_account_decoder:: { UiAccount , UiAccountData } ;
326
281
327
282
use crate :: posted_message:: MessageData ;
@@ -516,16 +471,21 @@ mod tests {
516
471
-----END WORMHOLE GUARDIAN PRIVATE KEY-----
517
472
"
518
473
. 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" ) ;
520
479
assert ! ( !guardian_key. unsafe_deterministic_key) ;
521
480
let secret_key = SecretKey :: from_slice ( & guardian_key. data )
522
481
. expect ( "Failed to create SecretKey from bytes" ) ;
482
+ let signer = signer:: FileSigner { secret_key } ;
523
483
assert_eq ! (
524
484
hex:: encode( secret_key. secret_bytes( ) ) ,
525
485
"f2f3127bff540c8441f99763f586858ef340c9962ad62b6181cd77203e81808f" ,
526
486
) ;
527
487
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 ) ,
529
489
"30e41be3f10d3ac813f91e49e189bbb948d030be" ,
530
490
) ;
531
491
}
0 commit comments