373 lines
11 KiB
Rust
373 lines
11 KiB
Rust
use solana_program::{
|
|
instruction::Instruction,
|
|
pubkey::Pubkey,
|
|
};
|
|
use std::str::FromStr;
|
|
|
|
use crate::vaa::{
|
|
DeserializePayload,
|
|
SignatureItem,
|
|
VAA,
|
|
};
|
|
use borsh::BorshDeserialize;
|
|
use byteorder::WriteBytesExt;
|
|
use sha3::Digest;
|
|
use solitaire::{
|
|
processors::seeded::Seeded,
|
|
AccountState,
|
|
};
|
|
use std::io::Write;
|
|
|
|
use crate::{
|
|
accounts::{
|
|
Bridge,
|
|
BridgeData,
|
|
FeeCollector,
|
|
GuardianSet,
|
|
GuardianSetData,
|
|
GuardianSetDerivationData,
|
|
PostedVAA,
|
|
PostedVAAData,
|
|
PostedVAADerivationData,
|
|
},
|
|
instructions::{
|
|
hash_vaa,
|
|
post_message,
|
|
post_vaa,
|
|
set_fees,
|
|
transfer_fees,
|
|
upgrade_contract,
|
|
upgrade_guardian_set,
|
|
verify_signatures,
|
|
},
|
|
types::{
|
|
ConsistencyLevel,
|
|
GovernancePayloadGuardianSetChange,
|
|
GovernancePayloadTransferFees,
|
|
GovernancePayloadUpgrade,
|
|
},
|
|
Claim,
|
|
ClaimDerivationData,
|
|
PostVAAData,
|
|
VerifySignaturesData,
|
|
};
|
|
use byteorder::LittleEndian;
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
#[wasm_bindgen]
|
|
pub fn post_message_ix(
|
|
program_id: String,
|
|
payer: String,
|
|
emitter: String,
|
|
message: String,
|
|
nonce: u32,
|
|
msg: Vec<u8>,
|
|
consistency: String,
|
|
) -> JsValue {
|
|
let consistency_level = match consistency.as_str() {
|
|
"CONFIRMED" => ConsistencyLevel::Confirmed,
|
|
"FINALIZED" => ConsistencyLevel::Finalized,
|
|
_ => panic!("invalid consistency level"),
|
|
};
|
|
let ix = post_message(
|
|
Pubkey::from_str(program_id.as_str()).unwrap(),
|
|
Pubkey::from_str(payer.as_str()).unwrap(),
|
|
Pubkey::from_str(emitter.as_str()).unwrap(),
|
|
Pubkey::from_str(message.as_str()).unwrap(),
|
|
nonce,
|
|
msg,
|
|
consistency_level,
|
|
)
|
|
.unwrap();
|
|
return JsValue::from_serde(&ix).unwrap();
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn post_vaa_ix(
|
|
program_id: String,
|
|
payer: String,
|
|
signature_set: String,
|
|
vaa: Vec<u8>,
|
|
) -> JsValue {
|
|
let vaa = VAA::deserialize(vaa.as_slice()).unwrap();
|
|
let vaa = PostVAAData {
|
|
version: vaa.version,
|
|
guardian_set_index: vaa.guardian_set_index,
|
|
timestamp: vaa.timestamp,
|
|
nonce: vaa.nonce,
|
|
emitter_chain: vaa.emitter_chain,
|
|
emitter_address: vaa.emitter_address,
|
|
sequence: vaa.sequence,
|
|
consistency_level: vaa.consistency_level,
|
|
payload: vaa.payload,
|
|
};
|
|
let ix = post_vaa(
|
|
Pubkey::from_str(program_id.as_str()).unwrap(),
|
|
Pubkey::from_str(payer.as_str()).unwrap(),
|
|
Pubkey::from_str(signature_set.as_str()).unwrap(),
|
|
vaa,
|
|
);
|
|
return JsValue::from_serde(&ix).unwrap();
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn update_guardian_set_ix(program_id: String, payer: String, vaa: Vec<u8>) -> JsValue {
|
|
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
|
|
let vaa = VAA::deserialize(vaa.as_slice()).unwrap();
|
|
let payload =
|
|
GovernancePayloadGuardianSetChange::deserialize(&mut vaa.payload.as_slice()).unwrap();
|
|
let message_key = PostedVAA::<'_, { AccountState::Uninitialized }>::key(
|
|
&PostedVAADerivationData {
|
|
payload_hash: hash_vaa(&vaa.clone().into()).to_vec(),
|
|
},
|
|
&program_id,
|
|
);
|
|
let ix = upgrade_guardian_set(
|
|
program_id,
|
|
Pubkey::from_str(payer.as_str()).unwrap(),
|
|
message_key,
|
|
Pubkey::new(&vaa.emitter_address),
|
|
payload.new_guardian_set_index - 1,
|
|
payload.new_guardian_set_index,
|
|
vaa.sequence,
|
|
);
|
|
return JsValue::from_serde(&ix).unwrap();
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn set_fees_ix(program_id: String, payer: String, vaa: Vec<u8>) -> JsValue {
|
|
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
|
|
let vaa = VAA::deserialize(vaa.as_slice()).unwrap();
|
|
let message_key = PostedVAA::<'_, { AccountState::Uninitialized }>::key(
|
|
&PostedVAADerivationData {
|
|
payload_hash: hash_vaa(&vaa.clone().into()).to_vec(),
|
|
},
|
|
&program_id,
|
|
);
|
|
let ix = set_fees(
|
|
program_id,
|
|
Pubkey::from_str(payer.as_str()).unwrap(),
|
|
message_key,
|
|
Pubkey::new(&vaa.emitter_address),
|
|
vaa.sequence,
|
|
);
|
|
return JsValue::from_serde(&ix).unwrap();
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn transfer_fees_ix(program_id: String, payer: String, vaa: Vec<u8>) -> JsValue {
|
|
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
|
|
let vaa = VAA::deserialize(vaa.as_slice()).unwrap();
|
|
let payload = GovernancePayloadTransferFees::deserialize(&mut vaa.payload.as_slice()).unwrap();
|
|
let message_key = PostedVAA::<'_, { AccountState::Uninitialized }>::key(
|
|
&PostedVAADerivationData {
|
|
payload_hash: hash_vaa(&vaa.clone().into()).to_vec(),
|
|
},
|
|
&program_id,
|
|
);
|
|
let ix = transfer_fees(
|
|
program_id,
|
|
Pubkey::from_str(payer.as_str()).unwrap(),
|
|
message_key,
|
|
Pubkey::new(&vaa.emitter_address),
|
|
vaa.sequence,
|
|
Pubkey::new(&payload.to[..]),
|
|
);
|
|
return JsValue::from_serde(&ix).unwrap();
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn upgrade_contract_ix(
|
|
program_id: String,
|
|
payer: String,
|
|
spill: String,
|
|
vaa: Vec<u8>,
|
|
) -> JsValue {
|
|
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
|
|
let spill = Pubkey::from_str(spill.as_str()).unwrap();
|
|
let vaa = VAA::deserialize(vaa.as_slice()).unwrap();
|
|
let payload = GovernancePayloadUpgrade::deserialize(&mut vaa.payload.as_slice()).unwrap();
|
|
let message_key = PostedVAA::<'_, { AccountState::Uninitialized }>::key(
|
|
&PostedVAADerivationData {
|
|
payload_hash: hash_vaa(&vaa.clone().into()).to_vec(),
|
|
},
|
|
&program_id,
|
|
);
|
|
let ix = upgrade_contract(
|
|
program_id,
|
|
Pubkey::from_str(payer.as_str()).unwrap(),
|
|
message_key,
|
|
Pubkey::new(&vaa.emitter_address),
|
|
payload.new_contract,
|
|
spill,
|
|
vaa.sequence,
|
|
);
|
|
return JsValue::from_serde(&ix).unwrap();
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn verify_signatures_ix(
|
|
program_id: String,
|
|
payer: String,
|
|
guardian_set_index: u32,
|
|
guardian_set: JsValue,
|
|
signature_set: String,
|
|
vaa_data: Vec<u8>,
|
|
) -> JsValue {
|
|
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
|
|
let payer = Pubkey::from_str(payer.as_str()).unwrap();
|
|
let signature_set = Pubkey::from_str(signature_set.as_str()).unwrap();
|
|
|
|
let guardian_set: GuardianSetData = guardian_set.into_serde().unwrap();
|
|
let vaa = VAA::deserialize(vaa_data.as_slice()).unwrap();
|
|
|
|
// Map signatures to guardian set
|
|
let mut signature_items: Vec<SignatureItem> = Vec::new();
|
|
for s in vaa.signatures.iter() {
|
|
let mut item = SignatureItem {
|
|
signature: s.signature.clone(),
|
|
key: [0; 20],
|
|
index: s.guardian_index as u8,
|
|
};
|
|
item.key = guardian_set.keys[s.guardian_index as usize];
|
|
|
|
signature_items.push(item);
|
|
}
|
|
|
|
let vaa_body = &vaa_data[VAA::HEADER_LEN + VAA::SIGNATURE_LEN * vaa.signatures.len()..];
|
|
let body_hash: [u8; 32] = {
|
|
let mut h = sha3::Keccak256::default();
|
|
h.write(vaa_body).unwrap();
|
|
h.finalize().into()
|
|
};
|
|
|
|
let mut verify_txs: Vec<Vec<Instruction>> = Vec::new();
|
|
for (_tx_index, chunk) in signature_items.chunks(7).enumerate() {
|
|
let mut secp_payload = Vec::new();
|
|
let mut signature_status = [-1i8; 19];
|
|
|
|
let data_offset = 1 + chunk.len() * 11;
|
|
let message_offset = data_offset + chunk.len() * 85;
|
|
|
|
// 1 number of signatures
|
|
secp_payload.write_u8(chunk.len() as u8).unwrap();
|
|
|
|
// Secp signature info description (11 bytes * n)
|
|
for (i, s) in chunk.iter().enumerate() {
|
|
secp_payload
|
|
.write_u16::<LittleEndian>((data_offset + 85 * i) as u16)
|
|
.unwrap();
|
|
secp_payload.write_u8(0).unwrap();
|
|
secp_payload
|
|
.write_u16::<LittleEndian>((data_offset + 85 * i + 65) as u16)
|
|
.unwrap();
|
|
secp_payload.write_u8(0).unwrap();
|
|
secp_payload
|
|
.write_u16::<LittleEndian>(message_offset as u16)
|
|
.unwrap();
|
|
secp_payload
|
|
.write_u16::<LittleEndian>(body_hash.len() as u16)
|
|
.unwrap();
|
|
secp_payload.write_u8(0).unwrap();
|
|
signature_status[s.index as usize] = i as i8;
|
|
}
|
|
|
|
// Write signatures and addresses
|
|
for s in chunk.iter() {
|
|
secp_payload.write(&s.signature).unwrap();
|
|
secp_payload.write(&s.key).unwrap();
|
|
}
|
|
|
|
// Write body
|
|
secp_payload.write(&body_hash).unwrap();
|
|
|
|
let secp_ix = Instruction {
|
|
program_id: solana_program::secp256k1_program::id(),
|
|
data: secp_payload,
|
|
accounts: vec![],
|
|
};
|
|
|
|
let payload = VerifySignaturesData {
|
|
signers: signature_status,
|
|
};
|
|
|
|
let verify_ix = match verify_signatures(
|
|
program_id,
|
|
payer,
|
|
guardian_set_index,
|
|
signature_set,
|
|
payload,
|
|
) {
|
|
Ok(v) => v,
|
|
Err(e) => panic!("{:?}", e),
|
|
};
|
|
|
|
verify_txs.push(vec![secp_ix, verify_ix])
|
|
}
|
|
|
|
JsValue::from_serde(&verify_txs).unwrap()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn guardian_set_address(bridge: String, index: u32) -> Vec<u8> {
|
|
let program_id = Pubkey::from_str(bridge.as_str()).unwrap();
|
|
let guardian_key = GuardianSet::<'_, { AccountState::Initialized }>::key(
|
|
&GuardianSetDerivationData { index: index },
|
|
&program_id,
|
|
);
|
|
|
|
guardian_key.to_bytes().to_vec()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn parse_guardian_set(data: Vec<u8>) -> JsValue {
|
|
JsValue::from_serde(&GuardianSetData::try_from_slice(data.as_slice()).unwrap()).unwrap()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn state_address(bridge: String) -> Vec<u8> {
|
|
let program_id = Pubkey::from_str(bridge.as_str()).unwrap();
|
|
let bridge_key = Bridge::<'_, { AccountState::Initialized }>::key(None, &program_id);
|
|
|
|
bridge_key.to_bytes().to_vec()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn parse_state(data: Vec<u8>) -> JsValue {
|
|
JsValue::from_serde(&BridgeData::try_from_slice(data.as_slice()).unwrap()).unwrap()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn fee_collector_address(bridge: String) -> Vec<u8> {
|
|
let program_id = Pubkey::from_str(bridge.as_str()).unwrap();
|
|
let bridge_key = FeeCollector::key(None, &program_id);
|
|
|
|
bridge_key.to_bytes().to_vec()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn claim_address(program_id: String, vaa: Vec<u8>) -> Vec<u8> {
|
|
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
|
|
|
|
let vaa = VAA::deserialize(vaa.as_slice()).unwrap();
|
|
let claim_key = Claim::<'_, { AccountState::Initialized }>::key(
|
|
&ClaimDerivationData {
|
|
emitter_address: vaa.emitter_address,
|
|
emitter_chain: vaa.emitter_chain,
|
|
sequence: vaa.sequence,
|
|
},
|
|
&program_id,
|
|
);
|
|
claim_key.to_bytes().to_vec()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn parse_posted_message(data: Vec<u8>) -> JsValue {
|
|
JsValue::from_serde(&PostedVAAData::try_from_slice(data.as_slice()).unwrap().0).unwrap()
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn parse_vaa(data: Vec<u8>) -> JsValue {
|
|
JsValue::from_serde(&VAA::deserialize(data.as_slice()).unwrap()).unwrap()
|
|
}
|