@@ -1124,18 +1124,31 @@ impl Processor {
1124
1124
accounts : & [ AccountInfo ] ,
1125
1125
lamports : u64 ,
1126
1126
transient_stake_seed : u64 ,
1127
+ maybe_ephemeral_stake_seed : Option < u64 > ,
1127
1128
) -> ProgramResult {
1128
1129
let account_info_iter = & mut accounts. iter ( ) ;
1129
1130
let stake_pool_info = next_account_info ( account_info_iter) ?;
1130
1131
let staker_info = next_account_info ( account_info_iter) ?;
1131
1132
let withdraw_authority_info = next_account_info ( account_info_iter) ?;
1132
1133
let validator_list_info = next_account_info ( account_info_iter) ?;
1133
1134
let validator_stake_account_info = next_account_info ( account_info_iter) ?;
1135
+ let maybe_ephemeral_stake_account_info = maybe_ephemeral_stake_seed
1136
+ . map ( |_| next_account_info ( account_info_iter) )
1137
+ . transpose ( ) ?;
1134
1138
let transient_stake_account_info = next_account_info ( account_info_iter) ?;
1135
1139
let clock_info = next_account_info ( account_info_iter) ?;
1136
1140
let clock = & Clock :: from_account_info ( clock_info) ?;
1137
- let rent_info = next_account_info ( account_info_iter) ?;
1138
- let rent = & Rent :: from_account_info ( rent_info) ?;
1141
+ let rent = if maybe_ephemeral_stake_seed. is_some ( ) {
1142
+ // instruction with ephemeral account doesn't take the rent account
1143
+ Rent :: get ( ) ?
1144
+ } else {
1145
+ // legacy instruction takes the rent account
1146
+ let rent_info = next_account_info ( account_info_iter) ?;
1147
+ Rent :: from_account_info ( rent_info) ?
1148
+ } ;
1149
+ let maybe_stake_history_info = maybe_ephemeral_stake_seed
1150
+ . map ( |_| next_account_info ( account_info_iter) )
1151
+ . transpose ( ) ?;
1139
1152
let system_program_info = next_account_info ( account_info_iter) ?;
1140
1153
let stake_program_info = next_account_info ( account_info_iter) ?;
1141
1154
@@ -1191,24 +1204,21 @@ impl Processor {
1191
1204
NonZeroU32 :: new ( validator_stake_info. validator_seed_suffix ) ,
1192
1205
) ?;
1193
1206
if validator_stake_info. transient_stake_lamports > 0 {
1194
- return Err ( StakePoolError :: TransientAccountInUse . into ( ) ) ;
1207
+ if maybe_ephemeral_stake_seed. is_none ( ) {
1208
+ msg ! ( "Attempting to decrease stake on a validator with pending transient stake, use DecreaseAdditionalValidatorStake with the existing seed" ) ;
1209
+ return Err ( StakePoolError :: TransientAccountInUse . into ( ) ) ;
1210
+ }
1211
+ if transient_stake_seed != validator_stake_info. transient_seed_suffix {
1212
+ msg ! (
1213
+ "Transient stake already exists with seed {}, you must use that one" ,
1214
+ validator_stake_info. transient_seed_suffix
1215
+ ) ;
1216
+ return Err ( ProgramError :: InvalidSeeds ) ;
1217
+ }
1218
+ // Let the runtime check to see if the merge is valid, so there's no
1219
+ // explicit check here that the transient stake is decreasing
1195
1220
}
1196
1221
1197
- let transient_stake_bump_seed = check_transient_stake_address (
1198
- program_id,
1199
- stake_pool_info. key ,
1200
- transient_stake_account_info. key ,
1201
- & vote_account_address,
1202
- transient_stake_seed,
1203
- ) ?;
1204
- let transient_stake_account_signer_seeds: & [ & [ _ ] ] = & [
1205
- TRANSIENT_STAKE_SEED_PREFIX ,
1206
- & vote_account_address. to_bytes ( ) ,
1207
- & stake_pool_info. key . to_bytes ( ) ,
1208
- & transient_stake_seed. to_le_bytes ( ) ,
1209
- & [ transient_stake_bump_seed] ,
1210
- ] ;
1211
-
1212
1222
let stake_minimum_delegation = stake:: tools:: get_minimum_delegation ( ) ?;
1213
1223
let stake_rent = rent. minimum_balance ( std:: mem:: size_of :: < stake:: state:: StakeState > ( ) ) ;
1214
1224
let current_minimum_lamports =
@@ -1236,38 +1246,126 @@ impl Processor {
1236
1246
return Err ( ProgramError :: InsufficientFunds ) ;
1237
1247
}
1238
1248
1239
- create_stake_account (
1240
- transient_stake_account_info. clone ( ) ,
1241
- transient_stake_account_signer_seeds,
1242
- system_program_info. clone ( ) ,
1243
- ) ?;
1249
+ let source_stake_account_info =
1250
+ if let Some ( ( ephemeral_stake_seed, ephemeral_stake_account_info) ) =
1251
+ maybe_ephemeral_stake_seed. zip ( maybe_ephemeral_stake_account_info)
1252
+ {
1253
+ let ephemeral_stake_bump_seed = check_ephemeral_stake_address (
1254
+ program_id,
1255
+ stake_pool_info. key ,
1256
+ ephemeral_stake_account_info. key ,
1257
+ ephemeral_stake_seed,
1258
+ ) ?;
1259
+ let ephemeral_stake_account_signer_seeds: & [ & [ _ ] ] = & [
1260
+ EPHEMERAL_STAKE_SEED_PREFIX ,
1261
+ & stake_pool_info. key . to_bytes ( ) ,
1262
+ & ephemeral_stake_seed. to_le_bytes ( ) ,
1263
+ & [ ephemeral_stake_bump_seed] ,
1264
+ ] ;
1265
+ create_stake_account (
1266
+ ephemeral_stake_account_info. clone ( ) ,
1267
+ ephemeral_stake_account_signer_seeds,
1268
+ system_program_info. clone ( ) ,
1269
+ ) ?;
1244
1270
1245
- // split into transient stake account
1246
- Self :: stake_split (
1247
- stake_pool_info. key ,
1248
- validator_stake_account_info. clone ( ) ,
1249
- withdraw_authority_info. clone ( ) ,
1250
- AUTHORITY_WITHDRAW ,
1251
- stake_pool. stake_withdraw_bump_seed ,
1252
- lamports,
1253
- transient_stake_account_info . clone ( ) ,
1254
- ) ?;
1271
+ // split into ephemeral stake account
1272
+ Self :: stake_split (
1273
+ stake_pool_info. key ,
1274
+ validator_stake_account_info. clone ( ) ,
1275
+ withdraw_authority_info. clone ( ) ,
1276
+ AUTHORITY_WITHDRAW ,
1277
+ stake_pool. stake_withdraw_bump_seed ,
1278
+ lamports,
1279
+ ephemeral_stake_account_info . clone ( ) ,
1280
+ ) ?;
1255
1281
1256
- // deactivate transient stake
1257
- Self :: stake_deactivate (
1258
- transient_stake_account_info. clone ( ) ,
1259
- clock_info. clone ( ) ,
1260
- withdraw_authority_info. clone ( ) ,
1282
+ Self :: stake_deactivate (
1283
+ ephemeral_stake_account_info. clone ( ) ,
1284
+ clock_info. clone ( ) ,
1285
+ withdraw_authority_info. clone ( ) ,
1286
+ stake_pool_info. key ,
1287
+ AUTHORITY_WITHDRAW ,
1288
+ stake_pool. stake_withdraw_bump_seed ,
1289
+ ) ?;
1290
+
1291
+ ephemeral_stake_account_info
1292
+ } else {
1293
+ // if no ephemeral account is provided, split everything from the
1294
+ // validator stake account, into the transient stake account
1295
+ validator_stake_account_info
1296
+ } ;
1297
+
1298
+ let transient_stake_bump_seed = check_transient_stake_address (
1299
+ program_id,
1261
1300
stake_pool_info. key ,
1262
- AUTHORITY_WITHDRAW ,
1263
- stake_pool. stake_withdraw_bump_seed ,
1301
+ transient_stake_account_info. key ,
1302
+ & vote_account_address,
1303
+ transient_stake_seed,
1264
1304
) ?;
1265
1305
1306
+ if validator_stake_info. transient_stake_lamports > 0 {
1307
+ let stake_history_info = maybe_stake_history_info. unwrap ( ) ;
1308
+ // transient stake exists, try to merge from the source account,
1309
+ // which is always an ephemeral account
1310
+ Self :: stake_merge (
1311
+ stake_pool_info. key ,
1312
+ source_stake_account_info. clone ( ) ,
1313
+ withdraw_authority_info. clone ( ) ,
1314
+ AUTHORITY_WITHDRAW ,
1315
+ stake_pool. stake_withdraw_bump_seed ,
1316
+ transient_stake_account_info. clone ( ) ,
1317
+ clock_info. clone ( ) ,
1318
+ stake_history_info. clone ( ) ,
1319
+ stake_program_info. clone ( ) ,
1320
+ ) ?;
1321
+ } else {
1322
+ let transient_stake_account_signer_seeds: & [ & [ _ ] ] = & [
1323
+ TRANSIENT_STAKE_SEED_PREFIX ,
1324
+ & vote_account_address. to_bytes ( ) ,
1325
+ & stake_pool_info. key . to_bytes ( ) ,
1326
+ & transient_stake_seed. to_le_bytes ( ) ,
1327
+ & [ transient_stake_bump_seed] ,
1328
+ ] ;
1329
+
1330
+ create_stake_account (
1331
+ transient_stake_account_info. clone ( ) ,
1332
+ transient_stake_account_signer_seeds,
1333
+ system_program_info. clone ( ) ,
1334
+ ) ?;
1335
+
1336
+ // split into transient stake account
1337
+ Self :: stake_split (
1338
+ stake_pool_info. key ,
1339
+ source_stake_account_info. clone ( ) ,
1340
+ withdraw_authority_info. clone ( ) ,
1341
+ AUTHORITY_WITHDRAW ,
1342
+ stake_pool. stake_withdraw_bump_seed ,
1343
+ lamports,
1344
+ transient_stake_account_info. clone ( ) ,
1345
+ ) ?;
1346
+
1347
+ // Deactivate transient stake if necessary
1348
+ let ( _, stake) = get_stake_state ( transient_stake_account_info) ?;
1349
+ if stake. delegation . deactivation_epoch == Epoch :: MAX {
1350
+ Self :: stake_deactivate (
1351
+ transient_stake_account_info. clone ( ) ,
1352
+ clock_info. clone ( ) ,
1353
+ withdraw_authority_info. clone ( ) ,
1354
+ stake_pool_info. key ,
1355
+ AUTHORITY_WITHDRAW ,
1356
+ stake_pool. stake_withdraw_bump_seed ,
1357
+ ) ?;
1358
+ }
1359
+ }
1360
+
1266
1361
validator_stake_info. active_stake_lamports = validator_stake_info
1267
1362
. active_stake_lamports
1268
1363
. checked_sub ( lamports)
1269
1364
. ok_or ( StakePoolError :: CalculationFailure ) ?;
1270
- validator_stake_info. transient_stake_lamports = lamports;
1365
+ validator_stake_info. transient_stake_lamports = validator_stake_info
1366
+ . transient_stake_lamports
1367
+ . checked_add ( lamports)
1368
+ . ok_or ( StakePoolError :: CalculationFailure ) ?;
1271
1369
validator_stake_info. transient_seed_suffix = transient_stake_seed;
1272
1370
1273
1371
Ok ( ( ) )
@@ -3248,6 +3346,21 @@ impl Processor {
3248
3346
accounts,
3249
3347
lamports,
3250
3348
transient_stake_seed,
3349
+ None ,
3350
+ )
3351
+ }
3352
+ StakePoolInstruction :: DecreaseAdditionalValidatorStake {
3353
+ lamports,
3354
+ transient_stake_seed,
3355
+ ephemeral_stake_seed,
3356
+ } => {
3357
+ msg ! ( "Instruction: DecreaseAdditionalValidatorStake" ) ;
3358
+ Self :: process_decrease_validator_stake (
3359
+ program_id,
3360
+ accounts,
3361
+ lamports,
3362
+ transient_stake_seed,
3363
+ Some ( ephemeral_stake_seed) ,
3251
3364
)
3252
3365
}
3253
3366
StakePoolInstruction :: IncreaseValidatorStake {
0 commit comments