refactor new ix processing, remove old processors
Change-Id: I682573bf8544c5a19b2b57c6660e9e86e9b12cf9
This commit is contained in:
parent
dfa746476f
commit
1f30398e30
|
@ -1,42 +1,37 @@
|
||||||
//! Program instruction processing logic
|
//! Program instruction processing logic
|
||||||
#![cfg(feature = "program")]
|
#![cfg(feature = "program")]
|
||||||
|
|
||||||
use std::{borrow::Borrow, cell::RefCell, io::Write, mem::size_of, slice::Iter};
|
use std::{io::Write, mem::size_of, slice::Iter};
|
||||||
|
|
||||||
use byteorder::ByteOrder;
|
use byteorder::ByteOrder;
|
||||||
use num_traits::AsPrimitive;
|
use num_traits::AsPrimitive;
|
||||||
use primitive_types::U256;
|
|
||||||
use sha3::Digest;
|
use sha3::Digest;
|
||||||
use solana_program::program::invoke_signed;
|
use solana_program::program::invoke_signed;
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
hash::Hasher,
|
|
||||||
info,
|
|
||||||
instruction::Instruction,
|
instruction::Instruction,
|
||||||
program_error::ProgramError,
|
program_error::ProgramError,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
system_instruction::{create_account, SystemInstruction},
|
system_instruction::{create_account},
|
||||||
sysvar::Sysvar,
|
sysvar::Sysvar,
|
||||||
};
|
};
|
||||||
use spl_token::{state::Mint};
|
|
||||||
|
|
||||||
|
use crate::instruction::PublishMessagePayload;
|
||||||
|
use crate::vaa::{BodyContractUpgrade, BodyMessage};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
instruction::{
|
instruction::{
|
||||||
BridgeInstruction, BridgeInstruction::*, TransferOutPayload, VAAData, VerifySigPayload,
|
BridgeInstruction, BridgeInstruction::*, VAAData, VerifySigPayload, CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_SOLANA, MAX_LEN_GUARDIAN_KEYS, MAX_VAA_SIZE,
|
MAX_LEN_GUARDIAN_KEYS, MAX_VAA_SIZE,
|
||||||
},
|
},
|
||||||
state::*,
|
state::*,
|
||||||
vaa::{BodyTransfer, BodyUpdateGuardianSet, VAABody, VAA},
|
vaa::{BodyUpdateGuardianSet, VAABody, VAA},
|
||||||
};
|
};
|
||||||
use solana_program::program_pack::Pack;
|
use crate::governance::{BodyUpdateGuardianSet, BodyContractUpgrade};
|
||||||
use std::borrow::BorrowMut;
|
|
||||||
use std::ops::Add;
|
|
||||||
use solana_program::fee_calculator::FeeCalculator;
|
|
||||||
use crate::vaa::BodyContractUpgrade;
|
|
||||||
|
|
||||||
/// SigInfo contains metadata about signers in a VerifySignature ix
|
/// SigInfo contains metadata about signers in a VerifySignature ix
|
||||||
struct SigInfo {
|
struct SigInfo {
|
||||||
|
@ -69,14 +64,10 @@ impl Bridge {
|
||||||
payload.config,
|
payload.config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TransferOut(p) => {
|
PublishMessage(p) => {
|
||||||
msg!("Instruction: TransferOut");
|
msg!("Instruction: PublishMessage");
|
||||||
|
|
||||||
if p.asset.chain == CHAIN_ID_SOLANA {
|
Self::process_publish_message(program_id, accounts, &p)
|
||||||
Self::process_transfer_native_out(program_id, accounts, &p)
|
|
||||||
} else {
|
|
||||||
Self::process_transfer_out(program_id, accounts, &p)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PostVAA(vaa_body) => {
|
PostVAA(vaa_body) => {
|
||||||
msg!("Instruction: PostVAA");
|
msg!("Instruction: PostVAA");
|
||||||
|
@ -84,21 +75,11 @@ impl Bridge {
|
||||||
|
|
||||||
Self::process_vaa(program_id, accounts, vaa_body, &vaa)
|
Self::process_vaa(program_id, accounts, vaa_body, &vaa)
|
||||||
}
|
}
|
||||||
PokeProposal() => {
|
|
||||||
msg!("Instruction: PokeProposal");
|
|
||||||
|
|
||||||
Self::process_poke(program_id, accounts)
|
|
||||||
}
|
|
||||||
VerifySignatures(p) => {
|
VerifySignatures(p) => {
|
||||||
msg!("Instruction: VerifySignatures");
|
msg!("Instruction: VerifySignatures");
|
||||||
|
|
||||||
Self::process_verify_signatures(program_id, accounts, &p)
|
Self::process_verify_signatures(program_id, accounts, &p)
|
||||||
}
|
}
|
||||||
CreateWrapped(meta) => {
|
|
||||||
msg!("Instruction: CreateWrapped");
|
|
||||||
Self::process_create_wrapped(program_id, accounts, &meta)
|
|
||||||
}
|
|
||||||
_ => panic!(""),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,23 +149,6 @@ impl Bridge {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transfers a wrapped asset out
|
|
||||||
pub fn process_poke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
|
||||||
let account_info_iter = &mut accounts.iter();
|
|
||||||
let proposal_info = Self::next_account_info_with_owner(account_info_iter, program_id)?;
|
|
||||||
|
|
||||||
let mut transfer_data = proposal_info.try_borrow_mut_data()?;
|
|
||||||
let mut proposal: &mut TransferOutProposal = Self::unpack(&mut transfer_data)?;
|
|
||||||
if proposal.vaa_time != 0 {
|
|
||||||
return Err(Error::VAAAlreadySubmitted.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increase poke counter
|
|
||||||
proposal.poke_counter += 1;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Processes signature verifications
|
/// Processes signature verifications
|
||||||
pub fn process_verify_signatures(
|
pub fn process_verify_signatures(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
@ -373,234 +337,76 @@ impl Bridge {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transfers a wrapped asset out
|
/// Publish a message to the Wormhole protocol
|
||||||
pub fn process_transfer_out(
|
pub fn process_publish_message(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
t: &TransferOutPayload,
|
t: &PublishMessagePayload,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
msg!("wrapped transfer out");
|
msg!("publishing message");
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
next_account_info(account_info_iter)?; // Bridge program
|
next_account_info(account_info_iter)?; // Bridge program
|
||||||
next_account_info(account_info_iter)?; // System program
|
next_account_info(account_info_iter)?; // System program
|
||||||
next_account_info(account_info_iter)?; // Token program
|
|
||||||
next_account_info(account_info_iter)?; // Rent sysvar
|
next_account_info(account_info_iter)?; // Rent sysvar
|
||||||
let clock_info = next_account_info(account_info_iter)?;
|
let clock_info = next_account_info(account_info_iter)?;
|
||||||
let instructions_info = next_account_info(account_info_iter)?;
|
let instructions_info = next_account_info(account_info_iter)?;
|
||||||
let sender_account_info = next_account_info(account_info_iter)?;
|
|
||||||
let bridge_info = Self::next_account_info_with_owner(account_info_iter, program_id)?;
|
let bridge_info = Self::next_account_info_with_owner(account_info_iter, program_id)?;
|
||||||
let transfer_info = next_account_info(account_info_iter)?;
|
let message_info = next_account_info(account_info_iter)?;
|
||||||
let mint_info = next_account_info(account_info_iter)?;
|
|
||||||
let payer_info = next_account_info(account_info_iter)?;
|
let payer_info = next_account_info(account_info_iter)?;
|
||||||
|
let emitter_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
|
||||||
let bridge_data = bridge_info.try_borrow_data()?;
|
|
||||||
let bridge: &Bridge = Self::unpack_immutable(&bridge_data)?;
|
|
||||||
let mint = Bridge::mint_deserialize(mint_info)?;
|
|
||||||
let clock = Clock::from_account_info(clock_info)?;
|
let clock = Clock::from_account_info(clock_info)?;
|
||||||
|
|
||||||
if *instructions_info.key != solana_program::sysvar::instructions::id() {
|
if *instructions_info.key != solana_program::sysvar::instructions::id() {
|
||||||
return Err(Error::InvalidSysvar.into());
|
return Err(Error::InvalidSysvar.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The message emitter must be a signer
|
||||||
|
if !emitter_info.is_signer {
|
||||||
|
return Err(Error::EmitterNotSigner.into());
|
||||||
|
}
|
||||||
|
|
||||||
// Fee handling
|
// Fee handling
|
||||||
let fee = Self::transfer_fee();
|
let fee = Self::transfer_fee();
|
||||||
Self::check_fees(instructions_info, bridge_info, fee)?;
|
Self::check_fees(instructions_info, bridge_info, fee)?;
|
||||||
|
|
||||||
// Does the token belong to the mint
|
|
||||||
if sender.mint != *mint_info.key {
|
|
||||||
return Err(Error::TokenMintMismatch.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the mint is actually a wrapped asset belonging to *this* bridge instance
|
|
||||||
let expected_mint_address = Bridge::derive_wrapped_asset_id(
|
|
||||||
program_id,
|
|
||||||
bridge_info.key,
|
|
||||||
t.asset.chain,
|
|
||||||
t.asset.decimals,
|
|
||||||
t.asset.address,
|
|
||||||
)?;
|
|
||||||
if expected_mint_address != *mint_info.key {
|
|
||||||
return Err(Error::InvalidDerivedAccount.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create transfer account
|
// Create transfer account
|
||||||
let transfer_seed = Bridge::derive_transfer_id_seeds(
|
let message_seed = Bridge::derive_message_seeds(
|
||||||
bridge_info.key,
|
bridge_info.key,
|
||||||
t.asset.chain,
|
CHAIN_ID_SOLANA,
|
||||||
t.asset.address,
|
emitter_info.key.to_bytes(),
|
||||||
t.chain_id,
|
|
||||||
t.target,
|
|
||||||
sender_account_info.key.to_bytes(),
|
|
||||||
t.nonce,
|
t.nonce,
|
||||||
|
&t.payload,
|
||||||
);
|
);
|
||||||
Bridge::check_and_create_account::<TransferOutProposal>(
|
Bridge::check_and_create_account::<PostedMessage>(
|
||||||
program_id,
|
program_id,
|
||||||
accounts,
|
accounts,
|
||||||
transfer_info.key,
|
message_info.key,
|
||||||
payer_info,
|
payer_info,
|
||||||
program_id,
|
program_id,
|
||||||
&transfer_seed,
|
&message_seed,
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Load transfer account
|
let mut message_data = message_info.try_borrow_mut_data()?;
|
||||||
let mut transfer_data = transfer_info.try_borrow_mut_data()?;
|
let mut message: &mut PostedMessage = Self::unpack(&mut message_data)?;
|
||||||
let mut transfer: &mut TransferOutProposal = Self::unpack(&mut transfer_data)?;
|
|
||||||
|
|
||||||
// Burn tokens
|
|
||||||
Bridge::wrapped_burn(
|
|
||||||
program_id,
|
|
||||||
accounts,
|
|
||||||
&bridge.config.token_program,
|
|
||||||
sender_account_info.key,
|
|
||||||
mint_info.key,
|
|
||||||
t.amount,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Initialize transfer
|
// Initialize transfer
|
||||||
transfer.nonce = t.nonce;
|
message.submission_time = clock.unix_timestamp as u32;
|
||||||
transfer.source_address = sender_account_info.key.to_bytes();
|
message.emitter_chain = CHAIN_ID_SOLANA;
|
||||||
transfer.foreign_address = t.target;
|
message.emitter_address = emitter_info.key.to_bytes();
|
||||||
transfer.amount = t.amount;
|
message.nonce = t.nonce;
|
||||||
transfer.to_chain_id = t.chain_id;
|
message.payload = t.payload;
|
||||||
transfer.lockup_time = clock.unix_timestamp as u32;
|
|
||||||
|
|
||||||
// Make sure decimals are correct
|
|
||||||
transfer.asset = AssetMeta {
|
|
||||||
chain: t.asset.chain, // Chain and address cannot be spoofed because the account is derived from it
|
|
||||||
address: t.asset.address,
|
|
||||||
decimals: mint.decimals, // We use the info from mint because it can be spoofed
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transfers a native token to a foreign chain
|
|
||||||
pub fn process_transfer_native_out(
|
|
||||||
program_id: &Pubkey,
|
|
||||||
accounts: &[AccountInfo],
|
|
||||||
t: &TransferOutPayload,
|
|
||||||
) -> ProgramResult {
|
|
||||||
msg!("native transfer out");
|
|
||||||
let account_info_iter = &mut accounts.iter();
|
|
||||||
next_account_info(account_info_iter)?; // Bridge program
|
|
||||||
next_account_info(account_info_iter)?; // System program
|
|
||||||
next_account_info(account_info_iter)?; // Token program
|
|
||||||
next_account_info(account_info_iter)?; // Rent sysvar
|
|
||||||
let clock_info = next_account_info(account_info_iter)?;
|
|
||||||
let instructions_info = next_account_info(account_info_iter)?;
|
|
||||||
let sender_account_info = next_account_info(account_info_iter)?;
|
|
||||||
let bridge_info = Self::next_account_info_with_owner(account_info_iter, program_id)?;
|
|
||||||
let transfer_info = next_account_info(account_info_iter)?;
|
|
||||||
let mint_info = next_account_info(account_info_iter)?;
|
|
||||||
let payer_info = next_account_info(account_info_iter)?;
|
|
||||||
let custody_info = next_account_info(account_info_iter)?;
|
|
||||||
|
|
||||||
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
|
||||||
let mint = Bridge::mint_deserialize(mint_info)?;
|
|
||||||
let bridge_data = bridge_info.try_borrow_data()?;
|
|
||||||
let bridge: &Bridge = Self::unpack_immutable(&bridge_data)?;
|
|
||||||
let clock = Clock::from_account_info(clock_info)?;
|
|
||||||
|
|
||||||
let fee = Self::transfer_fee();
|
|
||||||
Self::check_fees(instructions_info, bridge_info, fee)?;
|
|
||||||
|
|
||||||
// Does the token belong to the mint
|
|
||||||
if sender.mint != *mint_info.key {
|
|
||||||
return Err(Error::TokenMintMismatch.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create transfer account
|
|
||||||
let transfer_seed = Bridge::derive_transfer_id_seeds(
|
|
||||||
bridge_info.key,
|
|
||||||
t.asset.chain,
|
|
||||||
t.asset.address,
|
|
||||||
t.chain_id,
|
|
||||||
t.target,
|
|
||||||
sender_account_info.key.to_bytes(),
|
|
||||||
t.nonce,
|
|
||||||
);
|
|
||||||
Bridge::check_and_create_account::<TransferOutProposal>(
|
|
||||||
program_id,
|
|
||||||
accounts,
|
|
||||||
transfer_info.key,
|
|
||||||
payer_info,
|
|
||||||
program_id,
|
|
||||||
&transfer_seed,
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Load transfer account
|
|
||||||
let mut transfer_data = transfer_info.try_borrow_mut_data()?;
|
|
||||||
let mut transfer: &mut TransferOutProposal = Self::unpack(&mut transfer_data)?;
|
|
||||||
|
|
||||||
// Check that custody account was derived correctly
|
|
||||||
let expected_custody_id =
|
|
||||||
Bridge::derive_custody_id(program_id, bridge_info.key, mint_info.key)?;
|
|
||||||
if expected_custody_id != *custody_info.key {
|
|
||||||
return Err(Error::InvalidDerivedAccount.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the account if it does not exist
|
|
||||||
if custody_info.data_is_empty() {
|
|
||||||
Bridge::create_custody_account(
|
|
||||||
program_id,
|
|
||||||
accounts,
|
|
||||||
&bridge.config.token_program,
|
|
||||||
bridge_info.key,
|
|
||||||
custody_info.key,
|
|
||||||
mint_info.key,
|
|
||||||
payer_info,
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bridge_authority = Self::derive_bridge_id(program_id)?;
|
|
||||||
|
|
||||||
// Check that the custody token account is owned by the derived key
|
|
||||||
let custody = Self::token_account_deserialize(custody_info)?;
|
|
||||||
if custody.owner != bridge_authority {
|
|
||||||
return Err(Error::WrongTokenAccountOwner.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the source is not the custody account
|
|
||||||
if custody_info.key == sender_account_info.key {
|
|
||||||
return Err(Error::WrongTokenAccountOwner.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
msg!("transferring");
|
|
||||||
// Transfer tokens to custody - This also checks that custody mint = mint
|
|
||||||
Bridge::token_transfer_caller(
|
|
||||||
program_id,
|
|
||||||
accounts,
|
|
||||||
&bridge.config.token_program,
|
|
||||||
sender_account_info.key,
|
|
||||||
custody_info.key,
|
|
||||||
&bridge_authority,
|
|
||||||
t.amount,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Initialize proposal
|
|
||||||
transfer.amount = t.amount;
|
|
||||||
transfer.to_chain_id = t.chain_id;
|
|
||||||
transfer.source_address = sender_account_info.key.to_bytes();
|
|
||||||
transfer.foreign_address = t.target;
|
|
||||||
transfer.nonce = t.nonce;
|
|
||||||
transfer.lockup_time = clock.unix_timestamp as u32;
|
|
||||||
|
|
||||||
// Don't use the user-given data as we don't check mint = AssetMeta.address
|
|
||||||
transfer.asset = AssetMeta {
|
|
||||||
chain: CHAIN_ID_SOLANA,
|
|
||||||
address: mint_info.key.to_bytes(),
|
|
||||||
decimals: mint.decimals,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that a certain fee was sent to the bridge in the preceding instruction
|
/// Verify that a certain fee was sent to the bridge in the preceding instruction
|
||||||
pub fn check_fees(instructions_info: &AccountInfo, bridge_info: &AccountInfo, fee: u64) -> Result<(), ProgramError> {
|
pub fn check_fees(
|
||||||
|
instructions_info: &AccountInfo,
|
||||||
|
bridge_info: &AccountInfo,
|
||||||
|
fee: u64,
|
||||||
|
) -> Result<(), ProgramError> {
|
||||||
let current_instruction = solana_program::sysvar::instructions::load_current_index(
|
let current_instruction = solana_program::sysvar::instructions::load_current_index(
|
||||||
&instructions_info.try_borrow_mut_data()?,
|
&instructions_info.try_borrow_mut_data()?,
|
||||||
);
|
);
|
||||||
|
@ -663,9 +469,13 @@ impl Bridge {
|
||||||
amount: u64,
|
amount: u64,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
let mut payer_balance = payer_account.try_borrow_mut_lamports()?;
|
let mut payer_balance = payer_account.try_borrow_mut_lamports()?;
|
||||||
**payer_balance = payer_balance.checked_sub(amount).ok_or(ProgramError::InsufficientFunds)?;
|
**payer_balance = payer_balance
|
||||||
|
.checked_sub(amount)
|
||||||
|
.ok_or(ProgramError::InsufficientFunds)?;
|
||||||
let mut recipient_balance = recipient_account.try_borrow_mut_lamports()?;
|
let mut recipient_balance = recipient_account.try_borrow_mut_lamports()?;
|
||||||
**recipient_balance = recipient_balance.checked_add(amount).ok_or(ProgramError::InvalidArgument)?;
|
**recipient_balance = recipient_balance
|
||||||
|
.checked_add(amount)
|
||||||
|
.ok_or(ProgramError::InvalidArgument)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -708,7 +518,9 @@ impl Bridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the guardian set is still active
|
// Check that the guardian set is still active
|
||||||
if guardian_set.expiration_time != 0 && (guardian_set.expiration_time as i64) < clock.unix_timestamp {
|
if guardian_set.expiration_time != 0
|
||||||
|
&& (guardian_set.expiration_time as i64) < clock.unix_timestamp
|
||||||
|
{
|
||||||
return Err(Error::GuardianSetExpired.into());
|
return Err(Error::GuardianSetExpired.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,11 +538,11 @@ impl Bridge {
|
||||||
return Err(ProgramError::InvalidAccountData);
|
return Err(ProgramError::InvalidAccountData);
|
||||||
}
|
}
|
||||||
|
|
||||||
let signature_count = (sig_state
|
let signature_count = sig_state
|
||||||
.signatures
|
.signatures
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|v| v.iter().filter(|v| **v != 0).count() != 0)
|
.filter(|v| v.iter().filter(|v| **v != 0).count() != 0)
|
||||||
.count() as u8);
|
.count() as u8;
|
||||||
// Check quorum
|
// Check quorum
|
||||||
// We're using a fixed point number transformation with 1 decimal to deal with rounding.
|
// We're using a fixed point number transformation with 1 decimal to deal with rounding.
|
||||||
// The cast to u16 exists to prevent issues where len_keys * 10 might overflow.
|
// The cast to u16 exists to prevent issues where len_keys * 10 might overflow.
|
||||||
|
@ -738,86 +550,23 @@ impl Bridge {
|
||||||
return Err(ProgramError::InvalidArgument);
|
return Err(ProgramError::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut evict_signatures = false;
|
|
||||||
let payload = vaa.payload.as_ref().ok_or(Error::InvalidVAAAction)?;
|
|
||||||
match payload {
|
|
||||||
VAABody::UpdateGuardianSet(v) => {
|
|
||||||
let mut bridge_data = bridge_info.try_borrow_mut_data()?;
|
|
||||||
let bridge: &mut Bridge = Self::unpack(&mut bridge_data)?;
|
|
||||||
|
|
||||||
Self::process_vaa_set_update(
|
// Process the message posting
|
||||||
program_id,
|
Self::process_vaa_message_post(
|
||||||
accounts,
|
|
||||||
account_info_iter,
|
|
||||||
&clock,
|
|
||||||
bridge_info,
|
|
||||||
payer_info,
|
|
||||||
bridge,
|
|
||||||
guardian_set,
|
|
||||||
&v,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
VAABody::Transfer(v) => {
|
|
||||||
if v.source_chain == CHAIN_ID_SOLANA {
|
|
||||||
Self::process_vaa_transfer_post(
|
|
||||||
program_id,
|
|
||||||
account_info_iter,
|
|
||||||
bridge_info,
|
|
||||||
vaa,
|
|
||||||
&v,
|
|
||||||
vaa_data,
|
|
||||||
sig_info.key,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let bridge_data = bridge_info.try_borrow_data()?;
|
|
||||||
let bridge: &Bridge = Self::unpack_immutable(&bridge_data)?;
|
|
||||||
evict_signatures = true;
|
|
||||||
Self::process_vaa_transfer(
|
|
||||||
program_id,
|
|
||||||
accounts,
|
|
||||||
account_info_iter,
|
|
||||||
bridge_info,
|
|
||||||
bridge,
|
|
||||||
&v,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VAABody::UpgradeContract(v) => {
|
|
||||||
if v.chain_id == CHAIN_ID_SOLANA {
|
|
||||||
evict_signatures = true;
|
|
||||||
Self::process_vaa_upgrade(
|
|
||||||
program_id,
|
|
||||||
accounts,
|
|
||||||
bridge_info,
|
|
||||||
v,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return Err(Error::InvalidChain.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}?;
|
|
||||||
|
|
||||||
// Check and create claim
|
|
||||||
let claim_seeds = Bridge::derive_claim_seeds(bridge_info.key, vaa.signature_body()?);
|
|
||||||
// Will fail if the Claim exists i.e. the VAA has already been claimed
|
|
||||||
Bridge::check_and_create_account::<ClaimedVAA>(
|
|
||||||
program_id,
|
program_id,
|
||||||
accounts,
|
account_info_iter,
|
||||||
claim_info.key,
|
bridge_info,
|
||||||
payer_info,
|
vaa,
|
||||||
program_id,
|
sig_info.key,
|
||||||
&claim_seeds,
|
|
||||||
Some(bridge_info),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// If the signatures are not needed anymore, evict them and reclaim rent.
|
|
||||||
// This should cover most of the costs of the guardian.
|
|
||||||
if evict_signatures {
|
|
||||||
Self::transfer_sol(sig_info, payer_info, sig_info.lamports())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refund tx fee if possible
|
// Refund tx fee if possible
|
||||||
if bridge_info.lamports().checked_sub(Self::MIN_BRIDGE_BALANCE).unwrap_or(0) >= Self::VAA_TX_FEE {
|
if bridge_info
|
||||||
|
.lamports()
|
||||||
|
.checked_sub(Self::MIN_BRIDGE_BALANCE)
|
||||||
|
.unwrap_or(0)
|
||||||
|
>= Self::VAA_TX_FEE
|
||||||
|
{
|
||||||
Self::transfer_sol(bridge_info, payer_info, Self::VAA_TX_FEE)?;
|
Self::transfer_sol(bridge_info, payer_info, Self::VAA_TX_FEE)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,7 +581,7 @@ impl Bridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes a Guardian set update
|
/// Processes a Guardian set update
|
||||||
pub fn process_vaa_set_update(
|
pub fn process_governance_guardian_update(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
account_info_iter: &mut Iter<AccountInfo>,
|
account_info_iter: &mut Iter<AccountInfo>,
|
||||||
|
@ -903,59 +652,47 @@ impl Bridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes a VAA post for data availability (for Solana -> foreign transfers)
|
/// Processes a VAA post for data availability (for Solana -> foreign transfers)
|
||||||
pub fn process_vaa_transfer_post(
|
pub fn process_vaa_message_post(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
account_info_iter: &mut Iter<AccountInfo>,
|
account_info_iter: &mut Iter<AccountInfo>,
|
||||||
bridge_info: &AccountInfo,
|
bridge_info: &AccountInfo,
|
||||||
vaa: &VAA,
|
vaa: &VAA,
|
||||||
b: &BodyTransfer,
|
|
||||||
vaa_data: VAAData,
|
|
||||||
sig_account: &Pubkey,
|
sig_account: &Pubkey,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
msg!("posting VAA");
|
msg!("posting VAA message");
|
||||||
let proposal_info = Self::next_account_info_with_owner(account_info_iter, program_id)?;
|
let message_account = Self::next_account_info_with_owner(account_info_iter, program_id)?;
|
||||||
|
|
||||||
// Check whether the proposal was derived correctly
|
// Check whether the proposal was derived correctly
|
||||||
let expected_proposal = Bridge::derive_transfer_id(
|
let expected_message_address = Bridge::derive_message_id(
|
||||||
program_id,
|
program_id,
|
||||||
bridge_info.key,
|
bridge_info.key,
|
||||||
b.asset.chain,
|
b.emitter_chain,
|
||||||
b.asset.address,
|
b.emitter_address,
|
||||||
b.target_chain,
|
|
||||||
b.target_address,
|
|
||||||
b.source_address,
|
|
||||||
b.nonce,
|
b.nonce,
|
||||||
|
&b.data,
|
||||||
)?;
|
)?;
|
||||||
if expected_proposal != *proposal_info.key {
|
if expected_message_address != *message_account.key {
|
||||||
return Err(Error::InvalidDerivedAccount.into());
|
return Err(Error::InvalidDerivedAccount.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut transfer_data = proposal_info.try_borrow_mut_data()?;
|
let mut message_data = message_account.try_borrow_mut_data()?;
|
||||||
let mut proposal: &mut TransferOutProposal = Self::unpack(&mut transfer_data)?;
|
let mut message: &mut PostedMessage = Self::unpack(&mut message_data)?;
|
||||||
if !proposal.matches_vaa(b) {
|
if !message.matches_vaa(b) {
|
||||||
return Err(Error::VAAProposalMismatch.into());
|
return Err(Error::VAAMessageMismatch.into());
|
||||||
}
|
}
|
||||||
if proposal.vaa_time != 0 {
|
if message.vaa_time != 0 {
|
||||||
return Err(Error::VAAAlreadySubmitted.into());
|
return Err(Error::VAAAlreadySubmitted.into());
|
||||||
}
|
}
|
||||||
if vaa_data.len() > MAX_VAA_SIZE {
|
|
||||||
return Err(Error::VAATooLong.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set vaa
|
message.vaa_version = vaa.version;
|
||||||
for i in 0..vaa_data.len() {
|
message.vaa_time = vaa.timestamp;
|
||||||
proposal.vaa[i] = vaa_data[i]
|
message.vaa_signature_account = *sig_account;
|
||||||
}
|
|
||||||
// Stop byte
|
|
||||||
proposal.vaa[vaa_data.len()] = 0xff;
|
|
||||||
proposal.vaa_time = vaa.timestamp;
|
|
||||||
proposal.signature_account = *sig_account;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes a VAA contract upgrade
|
/// Processes a VAA contract upgrade
|
||||||
pub fn process_vaa_upgrade(
|
pub fn process_governance_upgrade(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
bridge_info: &AccountInfo,
|
bridge_info: &AccountInfo,
|
||||||
|
@ -987,7 +724,7 @@ impl Bridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invoke_vec_seed<'a>(
|
pub fn invoke_vec_seed<'a>(
|
||||||
program_id: &Pubkey,
|
_program_id: &Pubkey,
|
||||||
instruction: &Instruction,
|
instruction: &Instruction,
|
||||||
account_infos: &[AccountInfo<'a>],
|
account_infos: &[AccountInfo<'a>],
|
||||||
seeds: &Vec<Vec<u8>>,
|
seeds: &Vec<Vec<u8>>,
|
||||||
|
@ -998,9 +735,10 @@ impl Bridge {
|
||||||
|
|
||||||
/// The amount of sol that needs to be held in the BridgeConfig account in order to make it
|
/// The amount of sol that needs to be held in the BridgeConfig account in order to make it
|
||||||
/// exempt of rent payments.
|
/// exempt of rent payments.
|
||||||
const MIN_BRIDGE_BALANCE: u64 = (((solana_program::rent::ACCOUNT_STORAGE_OVERHEAD + size_of::<Bridge>() as u64) *
|
const MIN_BRIDGE_BALANCE: u64 =
|
||||||
solana_program::rent::DEFAULT_LAMPORTS_PER_BYTE_YEAR) as f64
|
(((solana_program::rent::ACCOUNT_STORAGE_OVERHEAD + size_of::<Bridge>() as u64)
|
||||||
* solana_program::rent::DEFAULT_EXEMPTION_THRESHOLD) as u64;
|
* solana_program::rent::DEFAULT_LAMPORTS_PER_BYTE_YEAR) as f64
|
||||||
|
* solana_program::rent::DEFAULT_EXEMPTION_THRESHOLD) as u64;
|
||||||
|
|
||||||
/// Check that a key was derived correctly and create account
|
/// Check that a key was derived correctly and create account
|
||||||
pub fn check_and_create_account<T: Sized>(
|
pub fn check_and_create_account<T: Sized>(
|
||||||
|
@ -1037,7 +775,11 @@ impl Bridge {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
let bal = v.try_lamports()?;
|
let bal = v.try_lamports()?;
|
||||||
let rent = Rent::default().minimum_balance(size_of::<T>());
|
let rent = Rent::default().minimum_balance(size_of::<T>());
|
||||||
if bal.checked_sub(Self::MIN_BRIDGE_BALANCE).ok_or(ProgramError::InsufficientFunds)? >= rent {
|
if bal
|
||||||
|
.checked_sub(Self::MIN_BRIDGE_BALANCE)
|
||||||
|
.ok_or(ProgramError::InsufficientFunds)?
|
||||||
|
>= rent
|
||||||
|
{
|
||||||
// Refund rent to payer
|
// Refund rent to payer
|
||||||
Self::transfer_sol(v, payer, rent)?;
|
Self::transfer_sol(v, payer, rent)?;
|
||||||
}
|
}
|
||||||
|
@ -1049,7 +791,7 @@ impl Bridge {
|
||||||
|
|
||||||
/// Create a new account
|
/// Create a new account
|
||||||
fn create_account_raw<T: Sized>(
|
fn create_account_raw<T: Sized>(
|
||||||
program_id: &Pubkey,
|
_program_id: &Pubkey,
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
new_account: &Pubkey,
|
new_account: &Pubkey,
|
||||||
payer: &Pubkey,
|
payer: &Pubkey,
|
||||||
|
|
Loading…
Reference in New Issue