wormhole/solana/bridge/program/src/api/governance.rs

280 lines
7.5 KiB
Rust

use solitaire::*;
use solana_program::{
program::invoke_signed,
pubkey::Pubkey,
sysvar::rent::Rent,
};
use solitaire::{
processors::seeded::Seeded,
CreationLamports::Exempt,
};
use crate::{
accounts::{
Bridge,
GuardianSet,
GuardianSetDerivationData,
},
error::Error::{
InvalidFeeRecipient,
InvalidGovernanceKey,
InvalidGovernanceWithdrawal,
InvalidGuardianSetUpgrade,
},
types::{
GovernancePayloadGuardianSetChange,
GovernancePayloadSetMessageFee,
GovernancePayloadTransferFees,
GovernancePayloadUpgrade,
},
vaa::{
ClaimableVAA,
DeserializePayload,
},
CHAIN_ID_SOLANA,
};
// Confirm that a ClaimableVAA came from the correct chain, signed by the right emitter.
fn verify_claim<'a, T>(vaa: &ClaimableVAA<'a, T>) -> Result<()>
where
T: DeserializePayload,
{
let expected_emitter = std::env!("EMITTER_ADDRESS");
let current_emitter = format!(
"{}",
Pubkey::new_from_array(vaa.message.meta().emitter_address)
);
// Fail if the emitter is not the known governance key, or the emitting chain is not Solana.
if expected_emitter != current_emitter || vaa.message.meta().emitter_chain != CHAIN_ID_SOLANA {
Err(InvalidGovernanceKey.into())
} else {
Ok(())
}
}
#[derive(FromAccounts)]
pub struct UpgradeContract<'b> {
/// Payer for account creation (vaa-claim)
pub payer: Mut<Signer<Info<'b>>>,
/// Upgrade VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadUpgrade>,
/// PDA authority for the loader
pub upgrade_authority: Derive<Info<'b>, "upgrade">,
/// Spill address for the upgrade excess lamports
pub spill: Info<'b>,
}
impl<'b> InstructionContext<'b> for UpgradeContract<'b> {
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct UpgradeContractData {}
pub fn upgrade_contract(
ctx: &ExecutionContext,
accs: &mut UpgradeContract,
_data: UpgradeContractData,
) -> Result<()> {
verify_claim(&accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade(
ctx.program_id,
&accs.vaa.message.new_contract,
accs.upgrade_authority.key,
accs.spill.key,
);
let seeds = accs.upgrade_authority.self_bumped_seeds(None, ctx.program_id);
let seeds: Vec<&[u8]> = seeds.iter().map(|item| item.as_slice()).collect();
let seeds = seeds.as_slice();
invoke_signed(&upgrade_ix, ctx.accounts, &[seeds])?;
Ok(())
}
#[derive(FromAccounts)]
pub struct UpgradeGuardianSet<'b> {
/// Payer for account creation (vaa-claim)
pub payer: Mut<Signer<Info<'b>>>,
/// Bridge config
pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>,
/// GuardianSet change VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadGuardianSetChange>,
/// Old guardian set
pub guardian_set_old: GuardianSet<'b, { AccountState::Initialized }>,
/// New guardian set
pub guardian_set_new: Mut<GuardianSet<'b, { AccountState::Uninitialized }>>,
}
impl<'b> InstructionContext<'b> for UpgradeGuardianSet<'b> {
fn verify(&self, _program_id: &Pubkey) -> Result<()> {
if self.guardian_set_old.index != self.vaa.new_guardian_set_index - 1 {
return Err(InvalidGuardianSetUpgrade.into());
}
if self.bridge.guardian_set_index != self.vaa.new_guardian_set_index - 1 {
return Err(InvalidGuardianSetUpgrade.into());
}
Ok(())
}
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct UpgradeGuardianSetData {}
pub fn upgrade_guardian_set(
ctx: &ExecutionContext,
accs: &mut UpgradeGuardianSet,
_data: UpgradeGuardianSetData,
) -> Result<()> {
verify_claim(&accs.vaa)?;
accs.guardian_set_old.verify_derivation(
ctx.program_id,
&GuardianSetDerivationData {
index: accs.vaa.new_guardian_set_index - 1,
},
)?;
accs.guardian_set_new.verify_derivation(
ctx.program_id,
&GuardianSetDerivationData {
index: accs.vaa.new_guardian_set_index,
},
)?;
accs.vaa.claim(ctx, accs.payer.key)?;
// Set expiration time for the old set
accs.guardian_set_old.expiration_time =
accs.vaa.meta().vaa_time + accs.bridge.config.guardian_set_expiration_time;
// Initialize new guardian Set
accs.guardian_set_new.index = accs.vaa.new_guardian_set_index;
accs.guardian_set_new.creation_time = accs.vaa.meta().vaa_time;
accs.guardian_set_new.keys = accs.vaa.new_guardian_set.clone();
// Create new guardian set
// This is done after populating it to properly allocate space according to key vec length.
accs.guardian_set_new.create(
&GuardianSetDerivationData {
index: accs.guardian_set_new.index,
},
ctx,
accs.payer.key,
Exempt,
)?;
// Set guardian set index
accs.bridge.guardian_set_index = accs.vaa.new_guardian_set_index;
Ok(())
}
#[derive(FromAccounts)]
pub struct SetFees<'b> {
/// Payer for account creation (vaa-claim)
pub payer: Mut<Signer<Info<'b>>>,
/// Bridge config
pub bridge: Mut<Bridge<'b, { AccountState::Initialized }>>,
/// Governance VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadSetMessageFee>,
}
impl<'b> InstructionContext<'b> for SetFees<'b> {
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct SetFeesData {}
pub fn set_fees(ctx: &ExecutionContext, accs: &mut SetFees, _data: SetFeesData) -> Result<()> {
verify_claim(&accs.vaa)?;
accs.vaa.claim(ctx, accs.payer.key)?;
accs.bridge.config.fee = accs.vaa.fee.as_u64();
accs.bridge.config.fee_persistent = accs.vaa.persisted_fee.as_u64();
Ok(())
}
#[derive(FromAccounts)]
pub struct TransferFees<'b> {
/// Payer for account creation (vaa-claim)
pub payer: Mut<Signer<Info<'b>>>,
/// Bridge config
pub bridge: Bridge<'b, { AccountState::Initialized }>,
/// Governance VAA
pub vaa: ClaimableVAA<'b, GovernancePayloadTransferFees>,
/// Account collecting tx fees
pub fee_collector: Mut<Derive<Info<'b>, "fee_collector">>,
/// Fee recipient
pub recipient: Mut<Info<'b>>,
/// Rent calculator to check transfer sizes.
pub rent: Sysvar<'b, Rent>,
}
impl<'b> InstructionContext<'b> for TransferFees<'b> {
fn verify(&self, _program_id: &Pubkey) -> Result<()> {
if self.vaa.to != self.recipient.key.to_bytes() {
return Err(InvalidFeeRecipient.into());
}
Ok(())
}
}
#[derive(BorshDeserialize, BorshSerialize, Default)]
pub struct TransferFeesData {}
pub fn transfer_fees(
ctx: &ExecutionContext,
accs: &mut TransferFees,
_data: TransferFeesData,
) -> Result<()> {
verify_claim(&accs.vaa)?;
if accs
.fee_collector
.lamports()
.saturating_sub(accs.vaa.amount.as_u64())
< accs.rent.minimum_balance(accs.fee_collector.data_len())
{
return Err(InvalidGovernanceWithdrawal.into());
}
accs.vaa.claim(ctx, accs.payer.key)?;
// Transfer fees
let transfer_ix = solana_program::system_instruction::transfer(
accs.fee_collector.key,
accs.recipient.key,
accs.vaa.amount.as_u64(),
);
let seeds = accs.fee_collector.self_bumped_seeds(None, ctx.program_id);
let seeds: Vec<&[u8]> = seeds.iter().map(|item| item.as_slice()).collect();
let seeds = seeds.as_slice();
invoke_signed(&transfer_ix, ctx.accounts, &[seeds])?;
Ok(())
}