refactor message posting ix, remove old ixs

Change-Id: Idfa487a189ec2fb3ed2029ddce10fc02aef1255b
This commit is contained in:
Hendrik Hofstadt 2021-04-15 11:36:29 +02:00
parent aad19ff8f8
commit dfa746476f
2 changed files with 83 additions and 244 deletions

View File

@ -3,8 +3,8 @@
#![cfg(not(feature = "no-entrypoint"))] #![cfg(not(feature = "no-entrypoint"))]
use solana_program::{ use solana_program::{
account_info::AccountInfo,entrypoint, entrypoint::ProgramResult, program_error::PrintProgramError, account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
pubkey::Pubkey, program_error::PrintProgramError, pubkey::Pubkey,
}; };
use crate::{error::Error, state::Bridge}; use crate::{error::Error, state::Bridge};

View File

@ -3,20 +3,20 @@
use std::mem::size_of; use std::mem::size_of;
use primitive_types::U256;
use solana_program::{ use solana_program::{
instruction::{AccountMeta, Instruction}, instruction::{AccountMeta, Instruction},
program_error::ProgramError, program_error::ProgramError,
pubkey::Pubkey, pubkey::Pubkey,
}; };
use crate::instruction::BridgeInstruction::PublishMessage;
use crate::{ use crate::{
instruction::BridgeInstruction::{ instruction::BridgeInstruction::{Initialize, PostVAA, VerifySignatures},
CreateWrapped, Initialize, PokeProposal, PostVAA, TransferOut, VerifySignatures, state::{Bridge, BridgeConfig},
},
state::{AssetMeta, Bridge, BridgeConfig},
vaa::{VAABody, VAA}, vaa::{VAABody, VAA},
}; };
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Cursor, Read, Write};
/// chain id of this chain /// chain id of this chain
pub const CHAIN_ID_SOLANA: u8 = 1; 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; pub const MAX_LEN_GUARDIAN_KEYS: usize = 20;
/// maximum size of a posted VAA /// maximum size of a posted VAA
pub const MAX_VAA_SIZE: usize = 1000; 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 /// size of a foreign address in bytes
const FOREIGN_ADDRESS_SIZE: usize = 32; const FOREIGN_ADDRESS_SIZE: usize = 32;
@ -45,34 +47,21 @@ pub struct InitializePayload {
pub config: BridgeConfig, pub config: BridgeConfig,
} }
#[repr(C)] pub struct PublishMessagePayload {
#[derive(Clone, Copy, Debug)] /// unique nonce for this message
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 nonce: u32, pub nonce: u32,
/// message payload
pub payload: Vec<u8>,
} }
#[repr(C)] impl Clone for PublishMessagePayload {
#[derive(Clone, Copy, Debug)] fn clone(&self) -> PublishMessagePayload {
pub struct TransferOutPayloadRaw { let payload = self.payload.clone();
/// amount to transfer return PublishMessagePayload {
pub amount: [u8; 32], payload,
/// chain id to transfer to nonce: self.nonce,
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,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -98,50 +87,16 @@ pub enum BridgeInstruction {
/// 4. `[signer]` The fee payer for new account creation /// 4. `[signer]` The fee payer for new account creation
Initialize(InitializePayload), Initialize(InitializePayload),
/// Burns or locks a (wrapped) asset `token` from `sender` on the Solana chain. /// Publishes a message over the Wormhole network.
/// /// See docs for accounts
/// Wrapped asset transfer out PublishMessage(PublishMessagePayload),
/// 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),
/// Submits a VAA signed by `guardian` on a valid `proposal`. /// Submits a VAA signed by `guardian` on a valid `proposal`.
/// See docs for accounts /// See docs for accounts
PostVAA(VAAData), 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 /// Verifies signature instructions
VerifySignatures(VerifySigPayload), VerifySignatures(VerifySigPayload),
/// Creates a new wrapped asset
CreateWrapped(AssetMeta),
} }
impl BridgeInstruction { impl BridgeInstruction {
@ -157,32 +112,32 @@ impl BridgeInstruction {
Initialize(*payload) Initialize(*payload)
} }
1 => { 1 => {
let payload: &TransferOutPayloadRaw = unpack(input)?; let mut payload_data = Cursor::new(input);
let amount = U256::from_big_endian(&payload.amount);
TransferOut(TransferOutPayload { let nonce = payload_data
amount, .read_u32::<BigEndian>()
chain_id: payload.chain_id, .map_err(|_| ProgramError::InvalidArgument)?;
asset: payload.asset, let mut message_payload: Vec<u8> = vec![];
target: payload.target, payload_data
nonce: payload.nonce, .read(&mut message_payload)
}) .map_err(|_| ProgramError::InvalidArgument)?;
let payload: PublishMessagePayload = PublishMessagePayload {
nonce,
payload: message_payload,
};
PublishMessage(payload)
} }
2 => { 2 => {
let payload: VAAData = input[1..].to_vec(); let payload: VAAData = input[1..].to_vec();
PostVAA(payload) PostVAA(payload)
} }
5 => PokeProposal(), 3 => {
6 => {
let payload: &VerifySigPayload = unpack(input)?; let payload: &VerifySigPayload = unpack(input)?;
VerifySignatures(*payload) VerifySignatures(*payload)
} }
7 => {
let payload: &AssetMeta = unpack(input)?;
CreateWrapped(*payload)
}
_ => return Err(ProgramError::InvalidInstructionData), _ => return Err(ProgramError::InvalidInstructionData),
}) })
} }
@ -196,65 +151,36 @@ impl BridgeInstruction {
output.resize(size_of::<InitializePayload>() + 1, 0); output.resize(size_of::<InitializePayload>() + 1, 0);
output[0] = 0; output[0] = 0;
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
let value = unsafe { let value = unsafe {
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut InitializePayload) &mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut InitializePayload)
}; };
*value = payload; *value = payload;
} }
Self::TransferOut(payload) => { Self::PublishMessage(payload) => {
output.resize(size_of::<TransferOutPayloadRaw>() + 1, 0); let mut v: Cursor<Vec<u8>> = Cursor::new(Vec::new());
output[0] = 1; v.write_u8(1).map_err(|_| ProgramError::InvalidArgument)?;
#[allow(clippy::cast_ptr_alignment)] v.write_u32::<BigEndian>(payload.nonce)
let value = unsafe { .map_err(|_| ProgramError::InvalidArgument)?;
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut TransferOutPayloadRaw) v.write(&payload.payload)
}; .map_err(|_| ProgramError::InvalidArgument)?;
let mut amount_bytes = [0u8; 32]; output = v.into_inner();
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,
};
} }
Self::PostVAA(payload) => { Self::PostVAA(payload) => {
output.resize(1, 0); output.resize(1, 0);
output[0] = 2; output[0] = 2;
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
output.extend_from_slice(&payload); 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;
} }
Self::VerifySignatures(payload) => { Self::VerifySignatures(payload) => {
output.resize(size_of::<VerifySigPayload>() + 1, 0); output.resize(size_of::<VerifySigPayload>() + 1, 0);
output[0] = 6; output[0] = 3;
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
let value = unsafe { let value = unsafe {
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut VerifySigPayload) &mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut VerifySigPayload)
}; };
*value = payload; *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) Ok(output)
} }
@ -280,7 +206,7 @@ pub fn initialize(
len_guardians: initial_guardian.len() as u8, len_guardians: initial_guardian.len() as u8,
initial_guardian: initial_g, initial_guardian: initial_g,
}) })
.serialize()?; .serialize()?;
let bridge_key = Bridge::derive_bridge_id(program_id)?; let bridge_key = Bridge::derive_bridge_id(program_id)?;
let guardian_set_key = Bridge::derive_guardian_set_id(program_id, &bridge_key, 0)?; 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"))] #[cfg(not(target_arch = "bpf"))]
pub fn transfer_out( pub fn publish_message(
program_id: &Pubkey, program_id: &Pubkey,
payer: &Pubkey, payer: &Pubkey,
token_account: &Pubkey, t: &PublishMessagePayload,
token_mint: &Pubkey,
t: &TransferOutPayload,
) -> Result<Instruction, ProgramError> { ) -> Result<Instruction, ProgramError> {
let data = BridgeInstruction::TransferOut(*t).serialize()?;
let bridge_key = Bridge::derive_bridge_id(program_id)?; 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, program_id,
&bridge_key, &bridge_key,
t.asset.chain, CHAIN_ID_SOLANA,
t.asset.address, payer.to_bytes(),
t.chain_id,
t.target,
token_account.to_bytes(),
t.nonce, t.nonce,
&t.payload,
)?; )?;
let mut accounts = vec![ let accounts = vec![
AccountMeta::new_readonly(*program_id, false), AccountMeta::new_readonly(*program_id, false),
AccountMeta::new_readonly(solana_program::system_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::rent::id(), false),
AccountMeta::new_readonly(solana_program::sysvar::clock::id(), false), AccountMeta::new_readonly(solana_program::sysvar::clock::id(), false),
AccountMeta::new_readonly(solana_program::sysvar::instructions::id(), false), AccountMeta::new_readonly(solana_program::sysvar::instructions::id(), false),
AccountMeta::new(*token_account, false),
AccountMeta::new_readonly(bridge_key, false), AccountMeta::new_readonly(bridge_key, false),
AccountMeta::new(transfer_key, false), AccountMeta::new(message_key, false),
AccountMeta::new(*token_mint, false),
AccountMeta::new(*payer, true), AccountMeta::new(*payer, true),
]; ];
// If the token is a native solana token add a custody account let data = BridgeInstruction::PublishMessage(t.clone()).serialize()?;
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));
}
Ok(Instruction { Ok(Instruction {
program_id: *program_id, program_id: *program_id,
@ -429,48 +343,26 @@ pub fn post_vaa(
// Make program writeable // Make program writeable
accounts[0] = AccountMeta::new(*program_id, false); accounts[0] = AccountMeta::new(*program_id, false);
accounts.push(AccountMeta::new(u.buffer, 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(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) => { VAABody::Message(t) => {
if t.source_chain == CHAIN_ID_SOLANA { let message_key = Bridge::derive_message_id(
// Solana (any) -> Ethereum (any) program_id,
let transfer_key = Bridge::derive_transfer_id( &bridge_key,
program_id, t.emitter_chain,
&bridge_key, t.emitter_address,
t.asset.chain, t.nonce,
t.asset.address, &t.data,
t.target_chain, )?;
t.target_address, accounts.push(AccountMeta::new(message_key, false))
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));
}
} }
} }
@ -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. /// Unpacks a reference from a bytes buffer.
pub fn unpack<T>(input: &[u8]) -> Result<&T, ProgramError> { pub fn unpack<T>(input: &[u8]) -> Result<&T, ProgramError> {
if input.len() < size_of::<u8>() + size_of::<T>() { if input.len() < size_of::<u8>() + size_of::<T>() {
return Err(ProgramError::InvalidInstructionData); return Err(ProgramError::InvalidInstructionData);
} }
#[allow(clippy::cast_ptr_alignment)] #[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) Ok(val)
} }