15
15
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
use core:: fmt;
17
17
use std:: collections:: HashSet ;
18
+ use std:: fs;
19
+ use std:: io:: Read ;
18
20
use std:: sync:: mpsc:: { Receiver , RecvTimeoutError } ;
19
21
use std:: thread:: JoinHandle ;
20
22
use std:: time:: { Duration , Instant } ;
@@ -1095,6 +1097,43 @@ impl RelayerThread {
1095
1097
debug ! ( "Relayer exit!" ) ;
1096
1098
}
1097
1099
1100
+ /// Try loading up a saved VRF key
1101
+ pub ( crate ) fn load_saved_vrf_key ( path : & str , pubkey_hash : & Hash160 ) -> Option < RegisteredKey > {
1102
+ let mut f = match fs:: File :: open ( path) {
1103
+ Ok ( f) => f,
1104
+ Err ( e) => {
1105
+ warn ! ( "Could not open {}: {:?}" , & path, & e) ;
1106
+ return None ;
1107
+ }
1108
+ } ;
1109
+ let mut registered_key_bytes = vec ! [ ] ;
1110
+ if let Err ( e) = f. read_to_end ( & mut registered_key_bytes) {
1111
+ warn ! (
1112
+ "Failed to read registered key bytes from {}: {:?}" ,
1113
+ path, & e
1114
+ ) ;
1115
+ return None ;
1116
+ }
1117
+
1118
+ let Ok ( registered_key) = serde_json:: from_slice :: < RegisteredKey > ( & registered_key_bytes)
1119
+ else {
1120
+ warn ! (
1121
+ "Did not load registered key from {}: could not decode JSON" ,
1122
+ & path
1123
+ ) ;
1124
+ return None ;
1125
+ } ;
1126
+
1127
+ // Check that the loaded key's memo matches the current miner's key
1128
+ if registered_key. memo != pubkey_hash. as_ref ( ) {
1129
+ warn ! ( "Loaded VRF key does not match mining key" ) ;
1130
+ return None ;
1131
+ }
1132
+
1133
+ info ! ( "Loaded registered key from {}" , & path) ;
1134
+ Some ( registered_key)
1135
+ }
1136
+
1098
1137
/// Top-level dispatcher
1099
1138
pub fn handle_directive ( & mut self , directive : RelayerDirective ) -> bool {
1100
1139
debug ! ( "Relayer: handling directive" ; "directive" => %directive) ;
@@ -1113,7 +1152,18 @@ impl RelayerThread {
1113
1152
info ! ( "In initial block download, will not submit VRF registration" ) ;
1114
1153
return true ;
1115
1154
}
1116
- self . rotate_vrf_and_register ( & last_burn_block) ;
1155
+ let mut saved_key_opt = None ;
1156
+ if let Some ( path) = self . config . miner . activated_vrf_key_path . as_ref ( ) {
1157
+ saved_key_opt =
1158
+ Self :: load_saved_vrf_key ( & path, & self . keychain . get_nakamoto_pkh ( ) ) ;
1159
+ }
1160
+ if let Some ( saved_key) = saved_key_opt {
1161
+ debug ! ( "Relayer: resuming VRF key" ) ;
1162
+ self . globals . resume_leader_key ( saved_key) ;
1163
+ } else {
1164
+ self . rotate_vrf_and_register ( & last_burn_block) ;
1165
+ debug ! ( "Relayer: directive Registered VRF key" ) ;
1166
+ }
1117
1167
self . globals . counters . bump_blocks_processed ( ) ;
1118
1168
true
1119
1169
}
@@ -1154,3 +1204,121 @@ impl RelayerThread {
1154
1204
continue_running
1155
1205
}
1156
1206
}
1207
+
1208
+ #[ cfg( test) ]
1209
+ pub mod test {
1210
+ use std:: fs:: File ;
1211
+ use std:: io:: Write ;
1212
+ use std:: path:: Path ;
1213
+
1214
+ use stacks:: util:: hash:: Hash160 ;
1215
+ use stacks:: util:: secp256k1:: Secp256k1PublicKey ;
1216
+ use stacks:: util:: vrf:: VRFPublicKey ;
1217
+
1218
+ use super :: RelayerThread ;
1219
+ use crate :: nakamoto_node:: save_activated_vrf_key;
1220
+ use crate :: run_loop:: RegisteredKey ;
1221
+ use crate :: Keychain ;
1222
+
1223
+ #[ test]
1224
+ fn load_nonexistent_vrf_key ( ) {
1225
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1226
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1227
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1228
+
1229
+ let path = "/tmp/does_not_exist.json" ;
1230
+ _ = std:: fs:: remove_file ( & path) ;
1231
+
1232
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1233
+ assert ! ( res. is_none( ) ) ;
1234
+ }
1235
+
1236
+ #[ test]
1237
+ fn load_empty_vrf_key ( ) {
1238
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1239
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1240
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1241
+
1242
+ let path = "/tmp/empty.json" ;
1243
+ File :: create ( & path) . expect ( "Failed to create test file" ) ;
1244
+ assert ! ( Path :: new( & path) . exists( ) ) ;
1245
+
1246
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1247
+ assert ! ( res. is_none( ) ) ;
1248
+
1249
+ std:: fs:: remove_file ( & path) . expect ( "Failed to delete test file" ) ;
1250
+ }
1251
+
1252
+ #[ test]
1253
+ fn load_bad_vrf_key ( ) {
1254
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1255
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1256
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1257
+
1258
+ let path = "/tmp/invalid_saved_key.json" ;
1259
+ let json_content = r#"{ "hello": "world" }"# ;
1260
+
1261
+ // Write the JSON content to the file
1262
+ let mut file = File :: create ( & path) . expect ( "Failed to create test file" ) ;
1263
+ file. write_all ( json_content. as_bytes ( ) )
1264
+ . expect ( "Failed to write to test file" ) ;
1265
+ assert ! ( Path :: new( & path) . exists( ) ) ;
1266
+
1267
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1268
+ assert ! ( res. is_none( ) ) ;
1269
+
1270
+ std:: fs:: remove_file ( & path) . expect ( "Failed to delete test file" ) ;
1271
+ }
1272
+
1273
+ #[ test]
1274
+ fn save_load_vrf_key ( ) {
1275
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1276
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1277
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1278
+ let key = RegisteredKey {
1279
+ target_block_height : 101 ,
1280
+ block_height : 102 ,
1281
+ op_vtxindex : 1 ,
1282
+ vrf_public_key : VRFPublicKey :: from_hex (
1283
+ "1da75863a7e1ef86f0f550d92b1f77dc60af23694b884b2816b703137ff94e71" ,
1284
+ )
1285
+ . unwrap ( ) ,
1286
+ memo : pubkey_hash. as_ref ( ) . to_vec ( ) ,
1287
+ } ;
1288
+ let path = "/tmp/vrf_key.json" ;
1289
+ save_activated_vrf_key ( path, & key) ;
1290
+
1291
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1292
+ assert ! ( res. is_some( ) ) ;
1293
+
1294
+ std:: fs:: remove_file ( & path) . expect ( "Failed to delete test file" ) ;
1295
+ }
1296
+
1297
+ #[ test]
1298
+ fn invalid_saved_memo ( ) {
1299
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1300
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1301
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1302
+ let key = RegisteredKey {
1303
+ target_block_height : 101 ,
1304
+ block_height : 102 ,
1305
+ op_vtxindex : 1 ,
1306
+ vrf_public_key : VRFPublicKey :: from_hex (
1307
+ "1da75863a7e1ef86f0f550d92b1f77dc60af23694b884b2816b703137ff94e71" ,
1308
+ )
1309
+ . unwrap ( ) ,
1310
+ memo : pubkey_hash. as_ref ( ) . to_vec ( ) ,
1311
+ } ;
1312
+ let path = "/tmp/vrf_key.json" ;
1313
+ save_activated_vrf_key ( path, & key) ;
1314
+
1315
+ let keychain = Keychain :: default ( vec ! [ 1u8 ; 32 ] ) ;
1316
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1317
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1318
+
1319
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1320
+ assert ! ( res. is_none( ) ) ;
1321
+
1322
+ std:: fs:: remove_file ( & path) . expect ( "Failed to delete test file" ) ;
1323
+ }
1324
+ }
0 commit comments