refactor message posting ix, remove old ixs
Change-Id: Idfa487a189ec2fb3ed2029ddce10fc02aef1255b
This commit is contained in:
parent
aad19ff8f8
commit
dfa746476f
|
@ -3,8 +3,8 @@
|
|||
#![cfg(not(feature = "no-entrypoint"))]
|
||||
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,entrypoint, entrypoint::ProgramResult, program_error::PrintProgramError,
|
||||
pubkey::Pubkey,
|
||||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
|
||||
program_error::PrintProgramError, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
use crate::{error::Error, state::Bridge};
|
||||
|
|
|
@ -3,20 +3,20 @@
|
|||
|
||||
use std::mem::size_of;
|
||||
|
||||
use primitive_types::U256;
|
||||
use solana_program::{
|
||||
instruction::{AccountMeta, Instruction},
|
||||
program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
use crate::instruction::BridgeInstruction::PublishMessage;
|
||||
use crate::{
|
||||
instruction::BridgeInstruction::{
|
||||
CreateWrapped, Initialize, PokeProposal, PostVAA, TransferOut, VerifySignatures,
|
||||
},
|
||||
state::{AssetMeta, Bridge, BridgeConfig},
|
||||
instruction::BridgeInstruction::{Initialize, PostVAA, VerifySignatures},
|
||||
state::{Bridge, BridgeConfig},
|
||||
vaa::{VAABody, VAA},
|
||||
};
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::io::{Cursor, Read, Write};
|
||||
|
||||
/// chain id of this chain
|
||||
pub const CHAIN_ID_SOLANA: u8 = 1;
|
||||
|
@ -24,6 +24,8 @@ pub const CHAIN_ID_SOLANA: u8 = 1;
|
|||
pub const MAX_LEN_GUARDIAN_KEYS: usize = 20;
|
||||
/// maximum size of a posted VAA
|
||||
pub const MAX_VAA_SIZE: usize = 1000;
|
||||
/// maximum size of a posted VAA
|
||||
pub const MAX_PAYLOAD_SIZE: usize = 400;
|
||||
/// size of a foreign address in bytes
|
||||
const FOREIGN_ADDRESS_SIZE: usize = 32;
|
||||
|
||||
|
@ -45,34 +47,21 @@ pub struct InitializePayload {
|
|||
pub config: BridgeConfig,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TransferOutPayload {
|
||||
/// amount to transfer
|
||||
pub amount: U256,
|
||||
/// chain id to transfer to
|
||||
pub chain_id: u8,
|
||||
/// Information about the asset to be transferred
|
||||
pub asset: AssetMeta,
|
||||
/// address on the foreign chain to transfer to
|
||||
pub target: ForeignAddress,
|
||||
/// unique nonce of the transfer
|
||||
pub struct PublishMessagePayload {
|
||||
/// unique nonce for this message
|
||||
pub nonce: u32,
|
||||
/// message payload
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TransferOutPayloadRaw {
|
||||
/// amount to transfer
|
||||
pub amount: [u8; 32],
|
||||
/// chain id to transfer to
|
||||
pub chain_id: u8,
|
||||
/// Information about the asset to be transferred
|
||||
pub asset: AssetMeta,
|
||||
/// address on the foreign chain to transfer to
|
||||
pub target: ForeignAddress,
|
||||
/// unique nonce of the transfer
|
||||
pub nonce: u32,
|
||||
impl Clone for PublishMessagePayload {
|
||||
fn clone(&self) -> PublishMessagePayload {
|
||||
let payload = self.payload.clone();
|
||||
return PublishMessagePayload {
|
||||
payload,
|
||||
nonce: self.nonce,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -98,50 +87,16 @@ pub enum BridgeInstruction {
|
|||
/// 4. `[signer]` The fee payer for new account creation
|
||||
Initialize(InitializePayload),
|
||||
|
||||
/// Burns or locks a (wrapped) asset `token` from `sender` on the Solana chain.
|
||||
///
|
||||
/// Wrapped asset transfer out
|
||||
/// 0. `[writable]` The from token account
|
||||
/// 1. `[]` The System program.
|
||||
/// 2. `[]` The spl token program.
|
||||
/// 3. `[]` The clock SysVar
|
||||
/// 4. `[derived]` The bridge config
|
||||
/// 5. `[writable, derived, empty]` The new transfer out tracking account
|
||||
/// 6. `[writable, derived]` The mint of the wrapped asset
|
||||
/// 7. ..7+M '[signer]' M signer accounts (from token authority)
|
||||
///
|
||||
/// Native token transfer out
|
||||
/// 0. `[writable]` The from token account
|
||||
/// 1. `[]` The System program.
|
||||
/// 2. `[]` The spl token program.
|
||||
/// 3. `[]` The clock SysVar
|
||||
/// 4. `[derived]` The bridge config
|
||||
/// 5. `[writable, derived, empty]` The new transfer out tracking account
|
||||
/// 6. `[writable, derived]` The mint of the wrapped asset
|
||||
/// 7. `[writable, derived]` The custody token account of the bridge
|
||||
/// 8. ..8+M '[signer]' M signer accounts (from token authority)
|
||||
TransferOut(TransferOutPayload),
|
||||
/// Publishes a message over the Wormhole network.
|
||||
/// See docs for accounts
|
||||
PublishMessage(PublishMessagePayload),
|
||||
|
||||
/// Submits a VAA signed by `guardian` on a valid `proposal`.
|
||||
/// See docs for accounts
|
||||
PostVAA(VAAData),
|
||||
|
||||
/// Deletes a `proposal` after the `VAA_EXPIRATION_TIME` is over to free up space on chain.
|
||||
/// This returns the rent to the sender.
|
||||
EvictTransferOut(),
|
||||
|
||||
/// Deletes a `ExecutedVAA` after the `VAA_EXPIRATION_TIME` is over to free up space on chain.
|
||||
/// This returns the rent to the sender.
|
||||
EvictClaimedVAA(),
|
||||
|
||||
/// Pokes a proposal with no valid VAAs attached so guardians reprocess it.
|
||||
PokeProposal(),
|
||||
|
||||
/// Verifies signature instructions
|
||||
VerifySignatures(VerifySigPayload),
|
||||
|
||||
/// Creates a new wrapped asset
|
||||
CreateWrapped(AssetMeta),
|
||||
}
|
||||
|
||||
impl BridgeInstruction {
|
||||
|
@ -157,32 +112,32 @@ impl BridgeInstruction {
|
|||
Initialize(*payload)
|
||||
}
|
||||
1 => {
|
||||
let payload: &TransferOutPayloadRaw = unpack(input)?;
|
||||
let amount = U256::from_big_endian(&payload.amount);
|
||||
let mut payload_data = Cursor::new(input);
|
||||
|
||||
TransferOut(TransferOutPayload {
|
||||
amount,
|
||||
chain_id: payload.chain_id,
|
||||
asset: payload.asset,
|
||||
target: payload.target,
|
||||
nonce: payload.nonce,
|
||||
})
|
||||
let nonce = payload_data
|
||||
.read_u32::<BigEndian>()
|
||||
.map_err(|_| ProgramError::InvalidArgument)?;
|
||||
let mut message_payload: Vec<u8> = vec![];
|
||||
payload_data
|
||||
.read(&mut message_payload)
|
||||
.map_err(|_| ProgramError::InvalidArgument)?;
|
||||
|
||||
let payload: PublishMessagePayload = PublishMessagePayload {
|
||||
nonce,
|
||||
payload: message_payload,
|
||||
};
|
||||
|
||||
PublishMessage(payload)
|
||||
}
|
||||
2 => {
|
||||
let payload: VAAData = input[1..].to_vec();
|
||||
PostVAA(payload)
|
||||
}
|
||||
5 => PokeProposal(),
|
||||
6 => {
|
||||
3 => {
|
||||
let payload: &VerifySigPayload = unpack(input)?;
|
||||
|
||||
VerifySignatures(*payload)
|
||||
}
|
||||
7 => {
|
||||
let payload: &AssetMeta = unpack(input)?;
|
||||
|
||||
CreateWrapped(*payload)
|
||||
}
|
||||
_ => return Err(ProgramError::InvalidInstructionData),
|
||||
})
|
||||
}
|
||||
|
@ -196,65 +151,36 @@ impl BridgeInstruction {
|
|||
output.resize(size_of::<InitializePayload>() + 1, 0);
|
||||
output[0] = 0;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let value = unsafe {
|
||||
let value = unsafe {
|
||||
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut InitializePayload)
|
||||
};
|
||||
*value = payload;
|
||||
}
|
||||
Self::TransferOut(payload) => {
|
||||
output.resize(size_of::<TransferOutPayloadRaw>() + 1, 0);
|
||||
output[0] = 1;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let value = unsafe {
|
||||
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut TransferOutPayloadRaw)
|
||||
};
|
||||
Self::PublishMessage(payload) => {
|
||||
let mut v: Cursor<Vec<u8>> = Cursor::new(Vec::new());
|
||||
v.write_u8(1).map_err(|_| ProgramError::InvalidArgument)?;
|
||||
v.write_u32::<BigEndian>(payload.nonce)
|
||||
.map_err(|_| ProgramError::InvalidArgument)?;
|
||||
v.write(&payload.payload)
|
||||
.map_err(|_| ProgramError::InvalidArgument)?;
|
||||
|
||||
let mut amount_bytes = [0u8; 32];
|
||||
payload.amount.to_big_endian(&mut amount_bytes);
|
||||
|
||||
*value = TransferOutPayloadRaw {
|
||||
amount: amount_bytes,
|
||||
chain_id: payload.chain_id,
|
||||
asset: payload.asset,
|
||||
target: payload.target,
|
||||
nonce: payload.nonce,
|
||||
};
|
||||
output = v.into_inner();
|
||||
}
|
||||
Self::PostVAA(payload) => {
|
||||
output.resize(1, 0);
|
||||
output[0] = 2;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
output.extend_from_slice(&payload);
|
||||
}
|
||||
Self::EvictTransferOut() => {
|
||||
output.resize(1, 0);
|
||||
output[0] = 3;
|
||||
}
|
||||
Self::EvictClaimedVAA() => {
|
||||
output.resize(1, 0);
|
||||
output[0] = 4;
|
||||
}
|
||||
Self::PokeProposal() => {
|
||||
output.resize(1, 0);
|
||||
output[0] = 5;
|
||||
output.extend_from_slice(&payload);
|
||||
}
|
||||
Self::VerifySignatures(payload) => {
|
||||
output.resize(size_of::<VerifySigPayload>() + 1, 0);
|
||||
output[0] = 6;
|
||||
output[0] = 3;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let value = unsafe {
|
||||
let value = unsafe {
|
||||
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut VerifySigPayload)
|
||||
};
|
||||
*value = payload;
|
||||
}
|
||||
Self::CreateWrapped(payload) => {
|
||||
output.resize(size_of::<AssetMeta>() + 1, 0);
|
||||
output[0] = 7;
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let value =
|
||||
unsafe { &mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut AssetMeta) };
|
||||
*value = payload;
|
||||
}
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
|
@ -280,7 +206,7 @@ pub fn initialize(
|
|||
len_guardians: initial_guardian.len() as u8,
|
||||
initial_guardian: initial_g,
|
||||
})
|
||||
.serialize()?;
|
||||
.serialize()?;
|
||||
|
||||
let bridge_key = Bridge::derive_bridge_id(program_id)?;
|
||||
let guardian_set_key = Bridge::derive_guardian_set_id(program_id, &bridge_key, 0)?;
|
||||
|
@ -300,48 +226,36 @@ pub fn initialize(
|
|||
})
|
||||
}
|
||||
|
||||
/// Creates an 'TransferOut' instruction.
|
||||
/// Creates an 'PublishMessage' instruction.
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
pub fn transfer_out(
|
||||
pub fn publish_message(
|
||||
program_id: &Pubkey,
|
||||
payer: &Pubkey,
|
||||
token_account: &Pubkey,
|
||||
token_mint: &Pubkey,
|
||||
t: &TransferOutPayload,
|
||||
t: &PublishMessagePayload,
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
let data = BridgeInstruction::TransferOut(*t).serialize()?;
|
||||
|
||||
let bridge_key = Bridge::derive_bridge_id(program_id)?;
|
||||
let transfer_key = Bridge::derive_transfer_id(
|
||||
|
||||
let message_key = Bridge::derive_message_id(
|
||||
program_id,
|
||||
&bridge_key,
|
||||
t.asset.chain,
|
||||
t.asset.address,
|
||||
t.chain_id,
|
||||
t.target,
|
||||
token_account.to_bytes(),
|
||||
CHAIN_ID_SOLANA,
|
||||
payer.to_bytes(),
|
||||
t.nonce,
|
||||
&t.payload,
|
||||
)?;
|
||||
|
||||
let mut accounts = vec![
|
||||
let accounts = vec![
|
||||
AccountMeta::new_readonly(*program_id, false),
|
||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
AccountMeta::new_readonly(solana_program::sysvar::rent::id(), false),
|
||||
AccountMeta::new_readonly(solana_program::sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(solana_program::sysvar::instructions::id(), false),
|
||||
AccountMeta::new(*token_account, false),
|
||||
AccountMeta::new_readonly(bridge_key, false),
|
||||
AccountMeta::new(transfer_key, false),
|
||||
AccountMeta::new(*token_mint, false),
|
||||
AccountMeta::new(message_key, false),
|
||||
AccountMeta::new(*payer, true),
|
||||
];
|
||||
|
||||
// If the token is a native solana token add a custody account
|
||||
if t.asset.chain == CHAIN_ID_SOLANA {
|
||||
let custody_key = Bridge::derive_custody_id(program_id, &bridge_key, token_mint)?;
|
||||
accounts.push(AccountMeta::new(custody_key, false));
|
||||
}
|
||||
let data = BridgeInstruction::PublishMessage(t.clone()).serialize()?;
|
||||
|
||||
Ok(Instruction {
|
||||
program_id: *program_id,
|
||||
|
@ -429,48 +343,26 @@ pub fn post_vaa(
|
|||
// Make program writeable
|
||||
accounts[0] = AccountMeta::new(*program_id, false);
|
||||
accounts.push(AccountMeta::new(u.buffer, false));
|
||||
let (programdata_address, _) = Pubkey::find_program_address(&[program_id.as_ref()], &solana_program::bpf_loader_upgradeable::id());
|
||||
let (programdata_address, _) = Pubkey::find_program_address(
|
||||
&[program_id.as_ref()],
|
||||
&solana_program::bpf_loader_upgradeable::id(),
|
||||
);
|
||||
accounts.push(AccountMeta::new(programdata_address, false));
|
||||
accounts.push(AccountMeta::new_readonly(solana_program::bpf_loader_upgradeable::id(), false));
|
||||
accounts.push(AccountMeta::new_readonly(
|
||||
solana_program::bpf_loader_upgradeable::id(),
|
||||
false,
|
||||
));
|
||||
}
|
||||
VAABody::Transfer(t) => {
|
||||
if t.source_chain == CHAIN_ID_SOLANA {
|
||||
// Solana (any) -> Ethereum (any)
|
||||
let transfer_key = Bridge::derive_transfer_id(
|
||||
program_id,
|
||||
&bridge_key,
|
||||
t.asset.chain,
|
||||
t.asset.address,
|
||||
t.target_chain,
|
||||
t.target_address,
|
||||
t.source_address,
|
||||
t.nonce,
|
||||
)?;
|
||||
accounts.push(AccountMeta::new(transfer_key, false))
|
||||
} else if t.asset.chain == CHAIN_ID_SOLANA {
|
||||
// Foreign (wrapped) -> Solana (native)
|
||||
let mint_key = Pubkey::new(&t.asset.address);
|
||||
let custody_key = Bridge::derive_custody_id(program_id, &bridge_key, &mint_key)?;
|
||||
accounts.push(AccountMeta::new_readonly(spl_token::id(), false));
|
||||
accounts.push(AccountMeta::new(mint_key, false));
|
||||
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
|
||||
accounts.push(AccountMeta::new(custody_key, false));
|
||||
} else {
|
||||
// Foreign (native) -> Solana (wrapped)
|
||||
let wrapped_key = Bridge::derive_wrapped_asset_id(
|
||||
program_id,
|
||||
&bridge_key,
|
||||
t.asset.chain,
|
||||
t.asset.decimals,
|
||||
t.asset.address,
|
||||
)?;
|
||||
let wrapped_meta_key =
|
||||
Bridge::derive_wrapped_meta_id(program_id, &bridge_key, &wrapped_key)?;
|
||||
accounts.push(AccountMeta::new_readonly(spl_token::id(), false));
|
||||
accounts.push(AccountMeta::new(wrapped_key, false));
|
||||
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
|
||||
accounts.push(AccountMeta::new(wrapped_meta_key, false));
|
||||
}
|
||||
VAABody::Message(t) => {
|
||||
let message_key = Bridge::derive_message_id(
|
||||
program_id,
|
||||
&bridge_key,
|
||||
t.emitter_chain,
|
||||
t.emitter_address,
|
||||
t.nonce,
|
||||
&t.data,
|
||||
)?;
|
||||
accounts.push(AccountMeta::new(message_key, false))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -481,65 +373,12 @@ pub fn post_vaa(
|
|||
})
|
||||
}
|
||||
|
||||
/// Creates a 'CreateWrapped' instruction.
|
||||
pub fn create_wrapped(
|
||||
program_id: &Pubkey,
|
||||
payer: &Pubkey,
|
||||
meta: AssetMeta,
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
let data = BridgeInstruction::CreateWrapped(meta).serialize()?;
|
||||
|
||||
let bridge_key = Bridge::derive_bridge_id(program_id)?;
|
||||
let wrapped_mint_key = Bridge::derive_wrapped_asset_id(
|
||||
program_id,
|
||||
&bridge_key,
|
||||
meta.chain,
|
||||
meta.decimals,
|
||||
meta.address,
|
||||
)?;
|
||||
let wrapped_meta_key =
|
||||
Bridge::derive_wrapped_meta_id(program_id, &bridge_key, &wrapped_mint_key)?;
|
||||
|
||||
let accounts = vec![
|
||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
AccountMeta::new_readonly(solana_program::sysvar::rent::id(), false),
|
||||
AccountMeta::new_readonly(bridge_key, false),
|
||||
AccountMeta::new(*payer, true),
|
||||
AccountMeta::new(wrapped_mint_key, false),
|
||||
AccountMeta::new(wrapped_meta_key, false),
|
||||
];
|
||||
|
||||
Ok(Instruction {
|
||||
program_id: *program_id,
|
||||
accounts,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates an 'PokeProposal' instruction.
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
pub fn poke_proposal(
|
||||
program_id: &Pubkey,
|
||||
transfer_proposal: &Pubkey,
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
let data = BridgeInstruction::PokeProposal().serialize()?;
|
||||
|
||||
let accounts = vec![AccountMeta::new(*transfer_proposal, false)];
|
||||
|
||||
Ok(Instruction {
|
||||
program_id: *program_id,
|
||||
accounts,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/// Unpacks a reference from a bytes buffer.
|
||||
pub fn unpack<T>(input: &[u8]) -> Result<&T, ProgramError> {
|
||||
if input.len() < size_of::<u8>() + size_of::<T>() {
|
||||
return Err(ProgramError::InvalidInstructionData);
|
||||
}
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let val: &T = unsafe { &*(&input[1] as *const u8 as *const T) };
|
||||
let val: &T = unsafe { &*(&input[1] as *const u8 as *const T) };
|
||||
Ok(val)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue