wormhole/solana/bridge/program/src/instructions.rs

410 lines
12 KiB
Rust

use borsh::BorshSerialize;
use solana_program::{
instruction::{
AccountMeta,
Instruction,
},
pubkey::Pubkey,
sysvar,
};
use solitaire::{
processors::seeded::Seeded,
AccountState,
};
use crate::{
accounts::{
Bridge,
Claim,
ClaimDerivationData,
FeeCollector,
GuardianSet,
GuardianSetDerivationData,
Message,
MessageDerivationData,
Sequence,
SequenceDerivationData,
SignatureSet,
SignatureSetDerivationData,
},
types::ConsistencyLevel,
InitializeData,
PayloadMessage,
PostMessageData,
PostVAAData,
SetFeesData,
TransferFeesData,
UpgradeContractData,
UpgradeGuardianSetData,
VerifySignaturesData,
CHAIN_ID_SOLANA,
};
pub fn initialize(
program_id: Pubkey,
payer: Pubkey,
fee: u64,
fee_persistent: u64,
guardian_set_expiration_time: u32,
initial_guardians: &[[u8; 20]],
) -> solitaire::Result<Instruction> {
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
&GuardianSetDerivationData { index: 0 },
&program_id,
);
let fee_collector = FeeCollector::key(None, &program_id);
Ok(Instruction {
program_id,
accounts: vec![
AccountMeta::new(bridge, false),
AccountMeta::new(guardian_set, false),
AccountMeta::new(fee_collector, false),
AccountMeta::new(payer, true),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
],
data: (crate::instruction::Instruction::Initialize, InitializeData {
initial_guardians: initial_guardians.to_vec(),
fee,
fee_persistent,
guardian_set_expiration_time,
})
.try_to_vec()?,
})
}
pub fn post_message(
program_id: Pubkey,
payer: Pubkey,
emitter: Pubkey,
nonce: u32,
payload: Vec<u8>,
persist: bool,
commitment: ConsistencyLevel,
) -> solitaire::Result<(Pubkey, Instruction)> {
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
let fee_collector = FeeCollector::<'_>::key(None, &program_id);
let sequence = Sequence::<'_>::key(
&SequenceDerivationData {
emitter_key: &emitter,
},
&program_id,
);
let message = Message::<'_, { AccountState::MaybeInitialized }>::key(
&MessageDerivationData {
emitter_key: emitter.to_bytes(),
emitter_chain: CHAIN_ID_SOLANA,
nonce,
sequence: None,
payload: payload.clone(),
},
&program_id,
);
Ok((
message,
Instruction {
program_id,
accounts: vec![
AccountMeta::new(bridge, false),
AccountMeta::new(message, false),
AccountMeta::new_readonly(emitter, true),
AccountMeta::new(sequence, false),
AccountMeta::new(payer, true),
AccountMeta::new(fee_collector, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
],
data: (crate::instruction::Instruction::PostMessage, PostMessageData {
nonce,
payload: payload.clone(),
persist,
consistency_level: commitment,
})
.try_to_vec()?,
},
))
}
pub fn verify_signatures(
program_id: Pubkey,
payer: Pubkey,
guardian_set_index: u32,
data: VerifySignaturesData,
) -> solitaire::Result<Instruction> {
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
&GuardianSetDerivationData {
index: guardian_set_index,
},
&program_id,
);
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
&SignatureSetDerivationData { hash: data.hash },
&program_id,
);
Ok(Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new_readonly(guardian_set, false),
AccountMeta::new(signature_set, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
],
data: (crate::instruction::Instruction::VerifySignatures, data).try_to_vec()?,
})
}
pub fn post_vaa(program_id: Pubkey, payer: Pubkey, vaa: PostVAAData) -> Instruction {
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
&GuardianSetDerivationData {
index: vaa.guardian_set_index,
},
&program_id,
);
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
&SignatureSetDerivationData {
hash: hash_vaa(&vaa),
},
&program_id,
);
let mut msg_derivation_data = MessageDerivationData {
emitter_key: vaa.emitter_address,
emitter_chain: vaa.emitter_chain,
nonce: vaa.nonce,
sequence: None,
payload: vaa.payload.clone(),
};
if vaa.emitter_chain != CHAIN_ID_SOLANA {
msg_derivation_data.sequence = Some(vaa.sequence);
}
let message =
Message::<'_, { AccountState::MaybeInitialized }>::key(&msg_derivation_data, &program_id);
Instruction {
program_id,
accounts: vec![
AccountMeta::new_readonly(guardian_set, false),
AccountMeta::new_readonly(bridge, false),
AccountMeta::new_readonly(signature_set, false),
AccountMeta::new(message, false),
AccountMeta::new(payer, true),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
],
data: (crate::instruction::Instruction::PostVAA, vaa)
.try_to_vec()
.unwrap(),
}
}
pub fn upgrade_contract(
program_id: Pubkey,
payer: Pubkey,
payload_message: Pubkey,
spill: Pubkey,
) -> Instruction {
let claim = Claim::<'_, { AccountState::Uninitialized }>::key(
&ClaimDerivationData {
emitter_address: [0u8; 32],
emitter_chain: CHAIN_ID_SOLANA,
sequence: 0,
},
&program_id,
);
let (upgrade_authority, _) =
Pubkey::find_program_address(&["upgrade_authority".as_bytes()], &program_id);
Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new_readonly(payload_message, false),
AccountMeta::new(claim, false),
AccountMeta::new_readonly(upgrade_authority, false),
AccountMeta::new_readonly(spill, false),
],
data: (crate::instruction::Instruction::UpgradeContract, UpgradeContractData {})
.try_to_vec()
.unwrap(),
}
}
pub fn upgrade_guardian_set(
program_id: Pubkey,
payer: Pubkey,
payload_message: Pubkey,
emitter: Pubkey,
old_index: u32,
new_index: u32,
sequence: u64,
) -> Instruction {
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
let claim = Claim::<'_, { AccountState::Uninitialized }>::key(
&ClaimDerivationData {
emitter_address: emitter.to_bytes(),
emitter_chain: CHAIN_ID_SOLANA,
sequence: sequence,
},
&program_id,
);
let guardian_set_old = GuardianSet::<'_, { AccountState::Initialized }>::key(
&GuardianSetDerivationData { index: old_index },
&program_id,
);
let guardian_set_new = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
&GuardianSetDerivationData { index: new_index },
&program_id,
);
Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new(bridge, false),
AccountMeta::new_readonly(payload_message, false),
AccountMeta::new(claim, false),
AccountMeta::new_readonly(guardian_set_old, false),
AccountMeta::new(guardian_set_new, false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
],
data: (crate::instruction::Instruction::UpgradeGuardianSet, UpgradeGuardianSetData {})
.try_to_vec()
.unwrap(),
}
}
pub fn set_fees(
program_id: Pubkey,
payer: Pubkey,
message: Pubkey,
emitter: Pubkey,
sequence: u64,
) -> Instruction {
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
let claim = Claim::<'_, { AccountState::Uninitialized }>::key(
&ClaimDerivationData {
emitter_address: emitter.to_bytes(),
emitter_chain: CHAIN_ID_SOLANA,
sequence,
},
&program_id,
);
Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new(bridge, false),
AccountMeta::new_readonly(message, false),
AccountMeta::new(claim, false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
],
data: (crate::instruction::Instruction::SetFees, SetFeesData {})
.try_to_vec()
.unwrap(),
}
}
pub fn transfer_fees(
program_id: Pubkey,
payer: Pubkey,
message: Pubkey,
emitter: Pubkey,
sequence: u64,
recipient: Pubkey,
) -> Instruction {
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
let claim = Claim::<'_, { AccountState::Uninitialized }>::key(
&ClaimDerivationData {
emitter_address: emitter.to_bytes(),
emitter_chain: CHAIN_ID_SOLANA,
sequence,
},
&program_id,
);
let fee_collector = FeeCollector::key(None, &program_id);
Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new_readonly(bridge, false),
AccountMeta::new_readonly(message, false),
AccountMeta::new(claim, false),
AccountMeta::new(fee_collector, false),
AccountMeta::new(recipient, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
],
data: (crate::instruction::Instruction::TransferFees, TransferFeesData {})
.try_to_vec()
.unwrap(),
}
}
// Convert a full VAA structure into the serialization of its unique components, this structure is
// what is hashed and verified by Guardians.
pub fn serialize_vaa(vaa: &PostVAAData) -> Vec<u8> {
use byteorder::{
BigEndian,
WriteBytesExt,
};
use std::io::{
Cursor,
Write,
};
let mut v = Cursor::new(Vec::new());
v.write_u32::<BigEndian>(vaa.timestamp).unwrap();
v.write_u32::<BigEndian>(vaa.nonce).unwrap();
v.write_u16::<BigEndian>(vaa.emitter_chain).unwrap();
v.write(&vaa.emitter_address).unwrap();
v.write_u64::<BigEndian>(vaa.sequence).unwrap();
v.write_u8(vaa.consistency_level).unwrap();
v.write(&vaa.payload).unwrap();
v.into_inner()
}
// Hash a VAA, this combines serialization and hashing.
pub fn hash_vaa(vaa: &PostVAAData) -> [u8; 32] {
use sha3::Digest;
use std::io::Write;
let body = serialize_vaa(vaa);
let mut h = sha3::Keccak256::default();
h.write(body.as_slice()).unwrap();
h.finalize().into()
}