Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for the increase/decrease additional stake instructions in the Python client library #6205

Merged
merged 18 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 140 additions & 47 deletions stake-pool/py/stake_pool/actions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Tuple
from typing import Optional, Tuple

from solana.keypair import Keypair
from solana.publickey import PublicKey
Expand All @@ -22,7 +22,8 @@
find_stake_program_address, \
find_transient_stake_program_address, \
find_withdraw_authority_program_address, \
find_metadata_account
find_metadata_account, \
find_ephemeral_stake_program_address
from stake_pool.state import STAKE_POOL_LAYOUT, ValidatorList, Fee, StakePool
import stake_pool.instructions as sp

Expand Down Expand Up @@ -480,8 +481,13 @@ async def update_stake_pool(client: AsyncClient, payer: Keypair, stake_pool_addr


async def increase_validator_stake(
client: AsyncClient, payer: Keypair, staker: Keypair, stake_pool_address: PublicKey,
validator_vote: PublicKey, lamports: int
client: AsyncClient,
payer: Keypair,
staker: Keypair,
stake_pool_address: PublicKey,
validator_vote: PublicKey,
lamports: int,
ephemeral_stake_seed: Optional[int] = None
):
resp = await client.get_account_info(stake_pool_address, commitment=Confirmed)
data = resp['result']['value']['data']
Expand All @@ -493,7 +499,13 @@ async def increase_validator_stake(
(withdraw_authority, seed) = find_withdraw_authority_program_address(STAKE_POOL_PROGRAM_ID, stake_pool_address)

validator_info = next(x for x in validator_list.validators if x.vote_account_address == validator_vote)
transient_stake_seed = validator_info.transient_seed_suffix + 1 # bump up by one to avoid reuse

if ephemeral_stake_seed is None:
transient_stake_seed = validator_info.transient_seed_suffix + 1 # bump up by one to avoid reuse
else:
# we are updating an existing transient stake account, so we must use the same seed
transient_stake_seed = validator_info.transient_seed_suffix

validator_stake_seed = validator_info.validator_seed_suffix or None
(transient_stake, _) = find_transient_stake_program_address(
STAKE_POOL_PROGRAM_ID,
Expand All @@ -509,38 +521,78 @@ async def increase_validator_stake(
)

txn = Transaction()
txn.add(
sp.increase_validator_stake(
sp.IncreaseValidatorStakeParams(
program_id=STAKE_POOL_PROGRAM_ID,
stake_pool=stake_pool_address,
staker=staker.public_key,
withdraw_authority=withdraw_authority,
validator_list=stake_pool.validator_list,
reserve_stake=stake_pool.reserve_stake,
transient_stake=transient_stake,
validator_stake=validator_stake,
validator_vote=validator_vote,
clock_sysvar=SYSVAR_CLOCK_PUBKEY,
rent_sysvar=SYSVAR_RENT_PUBKEY,
stake_history_sysvar=SYSVAR_STAKE_HISTORY_PUBKEY,
stake_config_sysvar=SYSVAR_STAKE_CONFIG_ID,
system_program_id=sys.SYS_PROGRAM_ID,
stake_program_id=STAKE_PROGRAM_ID,
lamports=lamports,
transient_stake_seed=transient_stake_seed,
if ephemeral_stake_seed is not None:

# We assume there is an existing transient account that we will update
(ephemeral_stake, _) = find_ephemeral_stake_program_address(
STAKE_POOL_PROGRAM_ID,
stake_pool_address,
ephemeral_stake_seed)

txn.add(
sp.increase_additional_validator_stake(
sp.IncreaseAdditionalValidatorStakeParams(
program_id=STAKE_POOL_PROGRAM_ID,
stake_pool=stake_pool_address,
staker=staker.public_key,
withdraw_authority=withdraw_authority,
validator_list=stake_pool.validator_list,
reserve_stake=stake_pool.reserve_stake,
transient_stake=transient_stake,
validator_stake=validator_stake,
validator_vote=validator_vote,
clock_sysvar=SYSVAR_CLOCK_PUBKEY,
rent_sysvar=SYSVAR_RENT_PUBKEY,
stake_history_sysvar=SYSVAR_STAKE_HISTORY_PUBKEY,
stake_config_sysvar=SYSVAR_STAKE_CONFIG_ID,
system_program_id=sys.SYS_PROGRAM_ID,
stake_program_id=STAKE_PROGRAM_ID,
lamports=lamports,
transient_stake_seed=transient_stake_seed,
ephemeral_stake=ephemeral_stake,
ephemeral_stake_seed=ephemeral_stake_seed
)
)
)

else:
txn.add(
sp.increase_validator_stake(
sp.IncreaseValidatorStakeParams(
program_id=STAKE_POOL_PROGRAM_ID,
stake_pool=stake_pool_address,
staker=staker.public_key,
withdraw_authority=withdraw_authority,
validator_list=stake_pool.validator_list,
reserve_stake=stake_pool.reserve_stake,
transient_stake=transient_stake,
validator_stake=validator_stake,
validator_vote=validator_vote,
clock_sysvar=SYSVAR_CLOCK_PUBKEY,
rent_sysvar=SYSVAR_RENT_PUBKEY,
stake_history_sysvar=SYSVAR_STAKE_HISTORY_PUBKEY,
stake_config_sysvar=SYSVAR_STAKE_CONFIG_ID,
system_program_id=sys.SYS_PROGRAM_ID,
stake_program_id=STAKE_PROGRAM_ID,
lamports=lamports,
transient_stake_seed=transient_stake_seed,
)
)
)
)

signers = [payer, staker] if payer != staker else [payer]
await client.send_transaction(
txn, *signers, opts=TxOpts(skip_confirmation=False, preflight_commitment=Confirmed))


async def decrease_validator_stake(
client: AsyncClient, payer: Keypair, staker: Keypair, stake_pool_address: PublicKey,
validator_vote: PublicKey, lamports: int
client: AsyncClient,
payer: Keypair,
staker: Keypair,
stake_pool_address: PublicKey,
validator_vote: PublicKey,
lamports: int,
ephemeral_stake_seed: Optional[int] = None
):
resp = await client.get_account_info(stake_pool_address, commitment=Confirmed)
data = resp['result']['value']['data']
Expand All @@ -559,7 +611,13 @@ async def decrease_validator_stake(
stake_pool_address,
validator_stake_seed,
)
transient_stake_seed = validator_info.transient_seed_suffix + 1 # bump up by one to avoid reuse

if ephemeral_stake_seed is None:
transient_stake_seed = validator_info.transient_seed_suffix + 1 # bump up by one to avoid reuse
else:
# we are updating an existing transient stake account, so we must use the same seed
transient_stake_seed = validator_info.transient_seed_suffix

(transient_stake, _) = find_transient_stake_program_address(
STAKE_POOL_PROGRAM_ID,
validator_info.vote_account_address,
Expand All @@ -568,26 +626,61 @@ async def decrease_validator_stake(
)

txn = Transaction()
txn.add(
sp.decrease_validator_stake_with_reserve(
sp.DecreaseValidatorStakeWithReserveParams(
program_id=STAKE_POOL_PROGRAM_ID,
stake_pool=stake_pool_address,
staker=staker.public_key,
withdraw_authority=withdraw_authority,
validator_list=stake_pool.validator_list,
reserve_stake=stake_pool.reserve_stake,
validator_stake=validator_stake,
transient_stake=transient_stake,
clock_sysvar=SYSVAR_CLOCK_PUBKEY,
stake_history_sysvar=SYSVAR_STAKE_HISTORY_PUBKEY,
system_program_id=sys.SYS_PROGRAM_ID,
stake_program_id=STAKE_PROGRAM_ID,
lamports=lamports,
transient_stake_seed=transient_stake_seed,

if ephemeral_stake_seed is not None:

# We assume there is an existing transient account that we will update
(ephemeral_stake, _) = find_ephemeral_stake_program_address(
STAKE_POOL_PROGRAM_ID,
stake_pool_address,
ephemeral_stake_seed)

txn.add(
sp.decrease_additional_validator_stake(
sp.DecreaseAdditionalValidatorStakeParams(
program_id=STAKE_POOL_PROGRAM_ID,
stake_pool=stake_pool_address,
staker=staker.public_key,
withdraw_authority=withdraw_authority,
validator_list=stake_pool.validator_list,
reserve_stake=stake_pool.reserve_stake,
validator_stake=validator_stake,
transient_stake=transient_stake,
clock_sysvar=SYSVAR_CLOCK_PUBKEY,
rent_sysvar=SYSVAR_RENT_PUBKEY,
stake_history_sysvar=SYSVAR_STAKE_HISTORY_PUBKEY,
system_program_id=sys.SYS_PROGRAM_ID,
stake_program_id=STAKE_PROGRAM_ID,
lamports=lamports,
transient_stake_seed=transient_stake_seed,
ephemeral_stake=ephemeral_stake,
ephemeral_stake_seed=ephemeral_stake_seed
)
)
)

else:

txn.add(
sp.decrease_validator_stake_with_reserve(
sp.DecreaseValidatorStakeWithReserveParams(
program_id=STAKE_POOL_PROGRAM_ID,
stake_pool=stake_pool_address,
staker=staker.public_key,
withdraw_authority=withdraw_authority,
validator_list=stake_pool.validator_list,
reserve_stake=stake_pool.reserve_stake,
validator_stake=validator_stake,
transient_stake=transient_stake,
clock_sysvar=SYSVAR_CLOCK_PUBKEY,
stake_history_sysvar=SYSVAR_STAKE_HISTORY_PUBKEY,
system_program_id=sys.SYS_PROGRAM_ID,
stake_program_id=STAKE_PROGRAM_ID,
lamports=lamports,
transient_stake_seed=transient_stake_seed,
)
)
)
)

signers = [payer, staker] if payer != staker else [payer]
await client.send_transaction(
Expand Down
19 changes: 19 additions & 0 deletions stake-pool/py/stake_pool/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ def find_transient_stake_program_address(
)


def find_ephemeral_stake_program_address(
program_id: PublicKey,
stake_pool_address: PublicKey,
seed: int
) -> Tuple[PublicKey, int]:

"""Generates the ephemeral program address for stake pool redelegation"""
return PublicKey.find_program_address(
[
EPHEMERAL_STAKE_SEED_PREFIX,
bytes(stake_pool_address),
seed.to_bytes(8, 'little'),
],
program_id,
)


def find_metadata_account(
mint_key: PublicKey
) -> Tuple[PublicKey, int]:
Expand All @@ -100,3 +117,5 @@ def find_metadata_account(
"""Seed used to derive transient stake accounts."""
METADATA_SEED_PREFIX = b"metadata"
"""Seed used to avoid certain collision attacks."""
EPHEMERAL_STAKE_SEED_PREFIX = b'ephemeral'
"""Seed for ephemeral stake account"""
Loading
Loading