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 } ;
@@ -110,6 +112,7 @@ pub struct LastCommit {
110
112
/// the tenure consensus hash for the tip's tenure
111
113
tenure_consensus_hash : ConsensusHash ,
112
114
/// the start-block hash of the tip's tenure
115
+ #[ allow( dead_code) ]
113
116
start_block_hash : BlockHeaderHash ,
114
117
/// What is the epoch in which this was sent?
115
118
epoch_id : StacksEpochId ,
@@ -836,14 +839,20 @@ impl RelayerThread {
836
839
} ) ?
837
840
} ;
838
841
839
- if last_winner_snapshot. miner_pk_hash != Some ( mining_pkh) {
840
- debug ! ( "Relayer: the miner did not win the last sortition. No tenure to continue." ;
841
- "current_mining_pkh" => %mining_pkh,
842
- "last_winner_snapshot.miner_pk_hash" => ?last_winner_snapshot. miner_pk_hash,
843
- ) ;
842
+ let won_last_sortition = last_winner_snapshot. miner_pk_hash == Some ( mining_pkh) ;
843
+ debug ! (
844
+ "Relayer: Current burn block had no sortition. Checking for tenure continuation." ;
845
+ "won_last_sortition" => won_last_sortition,
846
+ "current_mining_pkh" => %mining_pkh,
847
+ "last_winner_snapshot.miner_pk_hash" => ?last_winner_snapshot. miner_pk_hash,
848
+ "canonical_stacks_tip_id" => %canonical_stacks_tip,
849
+ "canonical_stacks_tip_ch" => %canonical_stacks_tip_ch,
850
+ "block_election_ch" => %block_election_snapshot. consensus_hash,
851
+ "burn_view_ch" => %new_burn_view,
852
+ ) ;
853
+
854
+ if !won_last_sortition {
844
855
return Ok ( ( ) ) ;
845
- } else {
846
- debug ! ( "Relayer: the miner won the last sortition. Continuing tenure." ) ;
847
856
}
848
857
849
858
match self . start_new_tenure (
@@ -1095,6 +1104,43 @@ impl RelayerThread {
1095
1104
debug ! ( "Relayer exit!" ) ;
1096
1105
}
1097
1106
1107
+ /// Try loading up a saved VRF key
1108
+ pub ( crate ) fn load_saved_vrf_key ( path : & str , pubkey_hash : & Hash160 ) -> Option < RegisteredKey > {
1109
+ let mut f = match fs:: File :: open ( path) {
1110
+ Ok ( f) => f,
1111
+ Err ( e) => {
1112
+ warn ! ( "Could not open {}: {:?}" , & path, & e) ;
1113
+ return None ;
1114
+ }
1115
+ } ;
1116
+ let mut registered_key_bytes = vec ! [ ] ;
1117
+ if let Err ( e) = f. read_to_end ( & mut registered_key_bytes) {
1118
+ warn ! (
1119
+ "Failed to read registered key bytes from {}: {:?}" ,
1120
+ path, & e
1121
+ ) ;
1122
+ return None ;
1123
+ }
1124
+
1125
+ let Ok ( registered_key) = serde_json:: from_slice :: < RegisteredKey > ( & registered_key_bytes)
1126
+ else {
1127
+ warn ! (
1128
+ "Did not load registered key from {}: could not decode JSON" ,
1129
+ & path
1130
+ ) ;
1131
+ return None ;
1132
+ } ;
1133
+
1134
+ // Check that the loaded key's memo matches the current miner's key
1135
+ if registered_key. memo != pubkey_hash. as_ref ( ) {
1136
+ warn ! ( "Loaded VRF key does not match mining key" ) ;
1137
+ return None ;
1138
+ }
1139
+
1140
+ info ! ( "Loaded registered key from {}" , & path) ;
1141
+ Some ( registered_key)
1142
+ }
1143
+
1098
1144
/// Top-level dispatcher
1099
1145
pub fn handle_directive ( & mut self , directive : RelayerDirective ) -> bool {
1100
1146
debug ! ( "Relayer: handling directive" ; "directive" => %directive) ;
@@ -1113,7 +1159,18 @@ impl RelayerThread {
1113
1159
info ! ( "In initial block download, will not submit VRF registration" ) ;
1114
1160
return true ;
1115
1161
}
1116
- self . rotate_vrf_and_register ( & last_burn_block) ;
1162
+ let mut saved_key_opt = None ;
1163
+ if let Some ( path) = self . config . miner . activated_vrf_key_path . as_ref ( ) {
1164
+ saved_key_opt =
1165
+ Self :: load_saved_vrf_key ( & path, & self . keychain . get_nakamoto_pkh ( ) ) ;
1166
+ }
1167
+ if let Some ( saved_key) = saved_key_opt {
1168
+ debug ! ( "Relayer: resuming VRF key" ) ;
1169
+ self . globals . resume_leader_key ( saved_key) ;
1170
+ } else {
1171
+ self . rotate_vrf_and_register ( & last_burn_block) ;
1172
+ debug ! ( "Relayer: directive Registered VRF key" ) ;
1173
+ }
1117
1174
self . globals . counters . bump_blocks_processed ( ) ;
1118
1175
true
1119
1176
}
@@ -1154,3 +1211,121 @@ impl RelayerThread {
1154
1211
continue_running
1155
1212
}
1156
1213
}
1214
+
1215
+ #[ cfg( test) ]
1216
+ pub mod test {
1217
+ use std:: fs:: File ;
1218
+ use std:: io:: Write ;
1219
+ use std:: path:: Path ;
1220
+
1221
+ use stacks:: util:: hash:: Hash160 ;
1222
+ use stacks:: util:: secp256k1:: Secp256k1PublicKey ;
1223
+ use stacks:: util:: vrf:: VRFPublicKey ;
1224
+
1225
+ use super :: RelayerThread ;
1226
+ use crate :: nakamoto_node:: save_activated_vrf_key;
1227
+ use crate :: run_loop:: RegisteredKey ;
1228
+ use crate :: Keychain ;
1229
+
1230
+ #[ test]
1231
+ fn load_nonexistent_vrf_key ( ) {
1232
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1233
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1234
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1235
+
1236
+ let path = "/tmp/does_not_exist.json" ;
1237
+ _ = std:: fs:: remove_file ( & path) ;
1238
+
1239
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1240
+ assert ! ( res. is_none( ) ) ;
1241
+ }
1242
+
1243
+ #[ test]
1244
+ fn load_empty_vrf_key ( ) {
1245
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1246
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1247
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1248
+
1249
+ let path = "/tmp/empty.json" ;
1250
+ File :: create ( & path) . expect ( "Failed to create test file" ) ;
1251
+ assert ! ( Path :: new( & path) . exists( ) ) ;
1252
+
1253
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1254
+ assert ! ( res. is_none( ) ) ;
1255
+
1256
+ std:: fs:: remove_file ( & path) . expect ( "Failed to delete test file" ) ;
1257
+ }
1258
+
1259
+ #[ test]
1260
+ fn load_bad_vrf_key ( ) {
1261
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1262
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1263
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1264
+
1265
+ let path = "/tmp/invalid_saved_key.json" ;
1266
+ let json_content = r#"{ "hello": "world" }"# ;
1267
+
1268
+ // Write the JSON content to the file
1269
+ let mut file = File :: create ( & path) . expect ( "Failed to create test file" ) ;
1270
+ file. write_all ( json_content. as_bytes ( ) )
1271
+ . expect ( "Failed to write to test file" ) ;
1272
+ assert ! ( Path :: new( & path) . exists( ) ) ;
1273
+
1274
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1275
+ assert ! ( res. is_none( ) ) ;
1276
+
1277
+ std:: fs:: remove_file ( & path) . expect ( "Failed to delete test file" ) ;
1278
+ }
1279
+
1280
+ #[ test]
1281
+ fn save_load_vrf_key ( ) {
1282
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1283
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1284
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1285
+ let key = RegisteredKey {
1286
+ target_block_height : 101 ,
1287
+ block_height : 102 ,
1288
+ op_vtxindex : 1 ,
1289
+ vrf_public_key : VRFPublicKey :: from_hex (
1290
+ "1da75863a7e1ef86f0f550d92b1f77dc60af23694b884b2816b703137ff94e71" ,
1291
+ )
1292
+ . unwrap ( ) ,
1293
+ memo : pubkey_hash. as_ref ( ) . to_vec ( ) ,
1294
+ } ;
1295
+ let path = "/tmp/vrf_key.json" ;
1296
+ save_activated_vrf_key ( path, & key) ;
1297
+
1298
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1299
+ assert ! ( res. is_some( ) ) ;
1300
+
1301
+ std:: fs:: remove_file ( & path) . expect ( "Failed to delete test file" ) ;
1302
+ }
1303
+
1304
+ #[ test]
1305
+ fn invalid_saved_memo ( ) {
1306
+ let keychain = Keychain :: default ( vec ! [ 0u8 ; 32 ] ) ;
1307
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1308
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1309
+ let key = RegisteredKey {
1310
+ target_block_height : 101 ,
1311
+ block_height : 102 ,
1312
+ op_vtxindex : 1 ,
1313
+ vrf_public_key : VRFPublicKey :: from_hex (
1314
+ "1da75863a7e1ef86f0f550d92b1f77dc60af23694b884b2816b703137ff94e71" ,
1315
+ )
1316
+ . unwrap ( ) ,
1317
+ memo : pubkey_hash. as_ref ( ) . to_vec ( ) ,
1318
+ } ;
1319
+ let path = "/tmp/vrf_key.json" ;
1320
+ save_activated_vrf_key ( path, & key) ;
1321
+
1322
+ let keychain = Keychain :: default ( vec ! [ 1u8 ; 32 ] ) ;
1323
+ let pk = Secp256k1PublicKey :: from_private ( keychain. get_nakamoto_sk ( ) ) ;
1324
+ let pubkey_hash = Hash160 :: from_node_public_key ( & pk) ;
1325
+
1326
+ let res = RelayerThread :: load_saved_vrf_key ( & path, & pubkey_hash) ;
1327
+ assert ! ( res. is_none( ) ) ;
1328
+
1329
+ std:: fs:: remove_file ( & path) . expect ( "Failed to delete test file" ) ;
1330
+ }
1331
+ }
0 commit comments