Skip to content

Commit

Permalink
closing #4
Browse files Browse the repository at this point in the history
  • Loading branch information
ochaloup committed May 9, 2024
1 parent 61f5ff6 commit 608cbac
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 73 deletions.
251 changes: 178 additions & 73 deletions settlement-pipelines/src/bin/close_settlements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ use settlement_pipelines::arguments::{
};
use settlement_pipelines::init::init_log;
use settlement_pipelines::settlements::list_expired_settlements;
use settlement_pipelines::stake_accounts::filter_settlement_funded;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signer::Signer;
use solana_sdk::stake::config::ID as stake_config_id;
use solana_sdk::stake::program::ID as stake_program_id;
use solana_sdk::stake::state::{Authorized, Lockup};
use solana_sdk::system_program;
use solana_sdk::sysvar::{
clock::ID as clock_sysvar_id, rent::ID as rent_sysvar_id,
stake_history::ID as stake_history_sysvar_id,
clock::ID as clock_sysvar_id, stake_history::ID as stake_history_sysvar_id,
};
use solana_sdk::{instruction::Instruction, packet::PACKET_DATA_SIZE, transaction::Transaction};
use solana_transaction_builder::TransactionBuilder;
use solana_transaction_builder_executor::{
builder_to_execution_data, execute_transactions_in_parallel,
Expand All @@ -26,17 +24,16 @@ use solana_transaction_executor::{
};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use validator_bonds::instructions::InitSettlementArgs;
use validator_bonds::state::bond::Bond;
use validator_bonds::state::config::find_bonds_withdrawer_authority;
use validator_bonds::state::settlement::find_settlement_staker_authority;
use validator_bonds::state::settlement::{find_settlement_staker_authority, Settlement};
use validator_bonds::ID as validator_bonds_id;
use validator_bonds_common::bonds::get_bonds_for_pubkeys;
use validator_bonds_common::config::get_config;
use validator_bonds_common::constants::find_event_authority;
use validator_bonds_common::settlement_claims::get_settlement_claims;
use validator_bonds_common::settlements::{get_settlements, get_settlements_for_pubkeys};
use validator_bonds_common::stake_accounts::{
collect_stake_accounts, is_locked, obtain_funded_stake_accounts_for_settlement,
};
use validator_bonds_common::settlements::get_settlements;
use validator_bonds_common::stake_accounts::{collect_stake_accounts, is_locked};
use validator_bonds_common::utils::get_sysvar_clock;

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -88,12 +85,164 @@ async fn main() -> anyhow::Result<()> {

let mut transaction_builder = TransactionBuilder::limited(fee_payer_keypair.clone());

let all_settlements = get_settlements(rpc_client.clone()).await?;

// Close Settlements
let expired_settlements =
list_expired_settlements(rpc_client.clone(), &config_address, &config).await?;
for (settlement_address, settlement) in expired_settlements {
let expired_settlements_bond_pubkeys = expired_settlements
.iter()
.map(|(_, settlement)| settlement.bond)
.collect::<HashSet<Pubkey>>()
.into_iter()
.collect::<Vec<Pubkey>>();
let bonds =
get_bonds_for_pubkeys(rpc_client.clone(), &expired_settlements_bond_pubkeys).await?;
let expired_settlements = expired_settlements
.into_iter()
.map(|(pubkey, settlement)| {
let bond = bonds
.iter()
.find(|(bond_pubkey, _)| bond_pubkey == &settlement.bond)
.map_or_else(
|| None,
|(_, bond)| {
if let Some(bond) = bond {
Some(bond.clone())
} else {
None
}
},
);
(pubkey, settlement, bond)
})
.collect::<Vec<(Pubkey, Settlement, Option<Bond>)>>();

// Before permitting to close settlements, we need to reset back all the stake accounts that were funded for the settlements
// otherwise the rest of the code is not permitted to run!
let clock = get_sysvar_clock(rpc_client.clone()).await?;
let config_stake_accounts =
collect_stake_accounts(rpc_client.clone(), Some(&bonds_withdrawer_authority), None).await?;
let settlement_funded_stake_accounts = filter_settlement_funded(config_stake_accounts, &clock);
let expired_settlements_staker_authorities = expired_settlements
.iter()
.map(|(settlement_address, settlement, bond)| {
(
find_settlement_staker_authority(settlement_address).0,
(settlement_address, settlement, bond),
)
})
// staker authority -> settlement
.collect::<HashMap<Pubkey, (&Pubkey, &Settlement, &Option<Bond>)>>();
for (stake_pubkey, _, stake_state) in settlement_funded_stake_accounts {
let staker_authority = if let Some(authorized) = stake_state.authorized() {
authorized.staker
} else {
// this should be already filtered out, not correctly settlement funded
continue;
};
let (settlement_address, settlement, bond) = if let Some(settlement) =
expired_settlements_staker_authorities.get(&staker_authority)
{
*settlement
} else {
// for another settlement that is not expired or does not exist
continue;
};

if let StakeStateV2::Initialized(_) = stake_state {
transaction_builder.add_signer_checked(&operator_authority_keypair);
// Initialized non-delegated can be withdrawn by operator
let req = program
.request()
.accounts(validator_bonds::accounts::WithdrawStake {
config: config_address,
operator_authority: operator_authority_keypair.pubkey(),
settlement: *settlement_address,
stake_account: stake_pubkey,
bonds_withdrawer_authority,
withdraw_to: args.marinade_wallet,
stake_history: stake_history_sysvar_id,
clock: clock_sysvar_id,
stake_program: stake_program_id,
program: validator_bonds_id,
event_authority: find_event_authority().0,
})
.args(validator_bonds::instruction::WithdrawStake {});
add_instruction_to_builder_from_anchor_with_description(
&mut transaction_builder,
&req,
format!(
"Withdraw un-claimed stake account {stake_pubkey} for settlement {}",
settlement_address
),
)?;
} else {
if let Some(bond) = bond {
// Delegated stake account can be reseted to a bond
let req = program
.request()
.accounts(validator_bonds::accounts::ResetStake {
config: config_address,
bond: settlement.bond,
settlement: *settlement_address,
stake_account: stake_pubkey,
bonds_withdrawer_authority,
vote_account: bond.vote_account,
stake_history: stake_history_sysvar_id,
stake_config: stake_config_id,
clock: clock_sysvar_id,
stake_program: stake_program_id,
program: validator_bonds_id,
event_authority: find_event_authority().0,
})
.args(validator_bonds::instruction::ResetStake {});
add_instruction_to_builder_from_anchor_with_description(
&mut transaction_builder,
&req,
format!(
"Withdraw un-claimed stake account {stake_pubkey} for settlement {}",
settlement_address
),
)?;
} else {
let error_msg = format!(
"For reset stake account {} (staker authority: {}) is required to know Settlement address but that was lost. Manual intervention needed.",
stake_pubkey, staker_authority
);
error!("{}", error_msg);
close_settlement_errors.push(error_msg);
}
}
}

// TODO: this HAS TO BE REFACTORED into function!
let transaction_executor_builder = TransactionExecutorBuilder::new()
.with_default_providers(rpc_client.clone())
.with_send_transaction_provider(SendTransactionWithGrowingTipProvider {
rpc_url: rpc_url.clone(),
query_param: "tip".into(),
tip_policy,
});
let transaction_executor = Arc::new(transaction_executor_builder.build());
let reset_stake_accounts_execution_count = transaction_builder.instructions().len();
let execution_data = builder_to_execution_data(
rpc_url.clone(),
&mut transaction_builder,
Some(priority_fee_policy.clone()),
);
execute_transactions_in_parallel(
transaction_executor.clone(),
execution_data,
Some(100_usize),
)
.await?;
info!("Reset and Withdraw StakeAccounts instructions {reset_stake_accounts_execution_count}",);
assert_eq!(
transaction_builder.instructions().len(),
0,
"Expected all instructions from builder are processed"
);

for (settlement_address, settlement, _) in expired_settlements.iter() {
let (settlement_staker_authority, _) =
find_settlement_staker_authority(&settlement_address);

Expand Down Expand Up @@ -142,7 +291,7 @@ async fn main() -> anyhow::Result<()> {
.accounts(validator_bonds::accounts::CloseSettlement {
config: config_address,
bond: settlement.bond,
settlement: settlement_address,
settlement: *settlement_address,
bonds_withdrawer_authority,
rent_collector: settlement.rent_collector,
split_rent_collector,
Expand All @@ -164,15 +313,6 @@ async fn main() -> anyhow::Result<()> {
)?;
}

// TODO: this HAS TO BE REFACTORED into function!
let transaction_executor_builder = TransactionExecutorBuilder::new()
.with_default_providers(rpc_client.clone())
.with_send_transaction_provider(SendTransactionWithGrowingTipProvider {
rpc_url: rpc_url.clone(),
query_param: "tip".into(),
tip_policy,
});
let transaction_executor = Arc::new(transaction_executor_builder.build());
let close_settlement_execution_count = transaction_builder.instructions().len();
let execution_data = builder_to_execution_data(
rpc_url.clone(),
Expand All @@ -189,7 +329,7 @@ async fn main() -> anyhow::Result<()> {
"CloseSettlement instructions {close_settlement_execution_count} executed successfully of settlements [{}]",
expired_settlements
.iter()
.map(|(p,_)| p.to_string())
.map(|(p,_, _)| p.to_string())
.collect::<Vec<String>>()
.join(", ")
);
Expand Down Expand Up @@ -234,21 +374,10 @@ async fn main() -> anyhow::Result<()> {
}
}

// Search what are funded stake accounts while settlements does not exist
let clock = get_sysvar_clock(rpc_client.clone()).await?;
// Verification of stake account existence that belongs to Settlements that does not exist
let config_stake_accounts =
collect_stake_accounts(rpc_client.clone(), Some(&bonds_withdrawer_authority), None).await?;
let settlement_funded_stake_accounts =
config_stake_accounts
.into_iter()
.filter(|(pubkey, _, state)| {
let is_settlement_funded = if let Some(authorized) = state.authorized() {
authorized.staker != authorized.withdrawer
} else {
false
};
is_settlement_funded && !is_locked(state, &clock)
});
let settlement_funded_stake_accounts = filter_settlement_funded(config_stake_accounts, &clock);
for (stake_pubkey, _, stake_state) in settlement_funded_stake_accounts {
let staker_authority = if let Some(authorized) = stake_state.authorized() {
authorized.staker
Expand All @@ -257,42 +386,18 @@ async fn main() -> anyhow::Result<()> {
// and cannot be reset or withdrawn
continue;
};
// when settlement for staker authority exists then skip
let settlement_address = if let(settlement_address) = existing_settlements_staker_authorities.get(&staker_authority) {
continue
} else {
Pubkey::default()
// already closed settlement - trouble to reset/withdraw
if existing_settlements_staker_authorities
.get(&staker_authority)
.is_none()
{
let error_msg = format!(
"For reset stake account {} (staker authority: {}) is required to know Settlement address but that was lost. Manual intervention needed.",
stake_pubkey, staker_authority
);
error!("{}", error_msg);
close_settlement_errors.push(error_msg);
};
if let StakeStateV2::Initialized(stake_meta) = stake_state {
transaction_builder.add_signer_checked(&operator_authority_keypair);
// Initialized non-delegated can be withdrawn by operator
let req = program
.request()
.accounts(validator_bonds::accounts::WithdrawStake {
config: config_address,
operator_authority: operator_authority_keypair.pubkey(),
settlement: settlement_address,
stake_account: stake_pubkey,
bonds_withdrawer_authority,
withdraw_to: args.marinade_wallet,
stake_history: stake_history_sysvar_id,
clock: clock_sysvar_id,
stake_program: stake_program_id,
program: validator_bonds_id,
event_authority: find_event_authority().0,
})
.args(validator_bonds::instruction::WithdrawStake {});
add_instruction_to_builder_from_anchor_with_description(
&mut transaction_builder,
&req,
format!(
"Withdraw un-claimed stake account {stake_pubkey} for settlement {}",
settlement_address
),
)?;
} else {
// Delegated stake account can be reseted to a bond
}
}

Ok(())
Expand Down
17 changes: 17 additions & 0 deletions settlement-pipelines/src/stake_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,20 @@ fn get_non_locked_priority_key(
255
}
}

pub fn filter_settlement_funded(
stake_accounts: CollectedStakeAccounts,
clock: &Clock,
) -> CollectedStakeAccounts {
stake_accounts
.into_iter()
.filter(|(_, _, state)| {
let is_settlement_funded = if let Some(authorized) = state.authorized() {
authorized.staker != authorized.withdrawer
} else {
false
};
is_settlement_funded && !is_locked(state, &clock)
})
.collect()
}

0 comments on commit 608cbac

Please sign in to comment.