Port over the Solana part of secp instruction checks
Change-Id: Ic205837a3a9a9010070596cbbab2590e15332527
This commit is contained in:
parent
09d2ecaaa1
commit
9dd36c6275
|
@ -112,6 +112,8 @@ name = "anchor-bridge"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"byteorder",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -237,9 +239,16 @@ version = "0.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"generic-array 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "0.8.2"
|
||||
|
@ -494,6 +503,12 @@ version = "0.4.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -736,6 +751,18 @@ dependencies = [
|
|||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest 0.9.0",
|
||||
"keccak",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-frozen-abi"
|
||||
version = "1.6.6"
|
||||
|
|
|
@ -16,3 +16,5 @@ default = []
|
|||
|
||||
[dependencies]
|
||||
anchor-lang = "0.4.4"
|
||||
byteorder = "1.4.3"
|
||||
sha3 = "0.9.1"
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
use anchor_lang::prelude::*;
|
||||
use anchor_lang::{solana_program, prelude::*};
|
||||
use byteorder::ByteOrder;
|
||||
use sha3::Digest;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct VerifySig<'info> {
|
||||
bridge: AccountInfo<'info>,
|
||||
system: AccountInfo<'info>,
|
||||
// TODO: Make this a `Sysvar<'info, Instructions>` like you can do
|
||||
// with `Rent` when `Sysvar` gets implemented for `Instructions` inside Solana.
|
||||
instruction_sysvar: AccountInfo<'info>,
|
||||
bridge_info: AccountInfo<'info>,
|
||||
sig_info: AccountInfo<'info>,
|
||||
|
@ -12,6 +18,7 @@ pub struct VerifySig<'info> {
|
|||
}
|
||||
|
||||
pub const MAX_LEN_GUARDIAN_KEYS: usize = 20;
|
||||
pub const MIN_SECP_PROGRAM_DATA_LEN: usize = 2;
|
||||
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, Debug)]
|
||||
pub struct VerifySigsData {
|
||||
|
@ -23,11 +30,140 @@ pub struct VerifySigsData {
|
|||
pub initial_creation: bool,
|
||||
}
|
||||
|
||||
/// SigInfo contains metadata about signers in a VerifySignature ix
|
||||
struct SigInfo {
|
||||
/// index of the signer in the guardianset
|
||||
signer_index: u8,
|
||||
/// index of the signature in the secp instruction
|
||||
sig_index: u8,
|
||||
}
|
||||
|
||||
struct SecpInstructionPart<'a> {
|
||||
address: &'a [u8],
|
||||
signature: &'a [u8],
|
||||
msg_offset: u16,
|
||||
msg_size: u16,
|
||||
}
|
||||
|
||||
#[program]
|
||||
pub mod anchor_bridge {
|
||||
use super::*;
|
||||
|
||||
pub fn verify_signatures(_ctx: Context<VerifySig>, _data: VerifySigsData) -> ProgramResult {
|
||||
pub fn verify_signatures(ctx: Context<VerifySig>, data: VerifySigsData) -> ProgramResult {
|
||||
|
||||
let sig_infos: Vec<SigInfo> = data
|
||||
.signers
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, p)| {
|
||||
if *p == -1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(SigInfo {
|
||||
sig_index: *p as u8,
|
||||
signer_index: i as u8,
|
||||
});
|
||||
})
|
||||
.collect();
|
||||
|
||||
// We check this manually because the type-level checks are
|
||||
// not available for Instructions yet. See the VerifySig
|
||||
// struct for more info.
|
||||
let ix_acc = &ctx.accounts.instruction_sysvar;
|
||||
if *ix_acc.key != solana_program::sysvar::instructions::id() {
|
||||
return Err(ProgramError::Custom(42));
|
||||
}
|
||||
|
||||
let current_ix_idx =
|
||||
solana_program::sysvar::instructions::load_current_index(&ix_acc.try_borrow_data()?);
|
||||
|
||||
if current_ix_idx == 0 {
|
||||
return Err(ProgramError::InvalidInstructionData);
|
||||
}
|
||||
|
||||
// Retrieve the previous instruction
|
||||
let prev_ix_idx = (current_ix_idx - 1) as u8;
|
||||
let prev_ix = solana_program::sysvar::instructions::load_instruction_at(
|
||||
prev_ix_idx as usize,
|
||||
&ix_acc.try_borrow_mut_data()?,
|
||||
)
|
||||
.map_err(|_e| ProgramError::InvalidAccountData)?;
|
||||
|
||||
// Does prev_ix call the right program?
|
||||
if prev_ix.program_id != solana_program::secp256k1_program::id() {
|
||||
return Err(ProgramError::InvalidArgument);
|
||||
}
|
||||
|
||||
// Is the data correctly sized?
|
||||
let prev_data_len = prev_ix.data.len();
|
||||
if prev_data_len < MIN_SECP_PROGRAM_DATA_LEN {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
}
|
||||
|
||||
// Parse the instruction data for verification
|
||||
let sig_len = prev_ix.data[0];
|
||||
let mut index = 1;
|
||||
|
||||
let mut secp_ixs: Vec<SecpInstructionPart> = Vec::with_capacity(sig_len as usize);
|
||||
for i in 0..sig_len {
|
||||
let sig_offset = byteorder::LE::read_u16(&prev_ix.data[index..index + 2]) as usize;
|
||||
index += 2;
|
||||
let sig_ix = prev_ix.data[index];
|
||||
index += 1;
|
||||
let address_offset = byteorder::LE::read_u16(&prev_ix.data[index..index + 2]) as usize;
|
||||
index += 2;
|
||||
let address_ix = prev_ix.data[index];
|
||||
index += 1;
|
||||
let msg_offset = byteorder::LE::read_u16(&prev_ix.data[index..index + 2]);
|
||||
index += 2;
|
||||
let msg_size = byteorder::LE::read_u16(&prev_ix.data[index..index + 2]);
|
||||
index += 2;
|
||||
let msg_ix = prev_ix.data[index];
|
||||
index += 1;
|
||||
|
||||
if address_ix != prev_ix_idx || msg_ix != prev_ix_idx || sig_ix != prev_ix_idx {
|
||||
return Err(ProgramError::InvalidArgument);
|
||||
}
|
||||
|
||||
let address: &[u8] = &prev_ix.data[address_offset..address_offset + 20];
|
||||
let signature: &[u8] = &prev_ix.data[sig_offset..sig_offset + 65];
|
||||
|
||||
// Make sure that all messages are equal
|
||||
if i > 0 {
|
||||
if msg_offset != secp_ixs[0].msg_offset || msg_size != secp_ixs[0].msg_size {
|
||||
return Err(ProgramError::InvalidArgument);
|
||||
}
|
||||
}
|
||||
secp_ixs.push(SecpInstructionPart {
|
||||
address,
|
||||
signature,
|
||||
msg_offset,
|
||||
msg_size,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if sig_infos.len() != secp_ixs.len() {
|
||||
return Err(ProgramError::InvalidArgument);
|
||||
}
|
||||
|
||||
// Check message
|
||||
let message = &prev_ix.data[secp_ixs[0].msg_offset as usize
|
||||
..(secp_ixs[0].msg_offset + secp_ixs[0].msg_size) as usize];
|
||||
|
||||
let mut h = sha3::Keccak256::default();
|
||||
if let Err(_) = h.write(message) {
|
||||
return Err(ProgramError::InvalidArgument);
|
||||
};
|
||||
let msg_hash: [u8; 32] = h.finalize().into();
|
||||
if msg_hash != data.hash {
|
||||
return Err(ProgramError::InvalidArgument);
|
||||
}
|
||||
|
||||
// ------ 8>< *SNIP <>8 --------
|
||||
// In original bridge program specific bridge state checks follow
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue