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"))]
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};

View File

@ -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),
})
}
@ -201,24 +156,15 @@ impl BridgeInstruction {
};
*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);
@ -226,35 +172,15 @@ impl BridgeInstruction {
#[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;
}
Self::VerifySignatures(payload) => {
output.resize(size_of::<VerifySigPayload>() + 1, 0);
output[0] = 6;
output[0] = 3;
#[allow(clippy::cast_ptr_alignment)]
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)
}
@ -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,103 +343,28 @@ 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(
VAABody::Message(t) => {
let message_key = Bridge::derive_message_id(
program_id,
&bridge_key,
t.asset.chain,
t.asset.address,
t.target_chain,
t.target_address,
t.source_address,
t.emitter_chain,
t.emitter_address,
t.nonce,
&t.data,
)?;
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));
accounts.push(AccountMeta::new(message_key, false))
}
}
}
Ok(Instruction {
program_id: *program_id,
accounts,
data,
})
}
/// 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,