Skip to content

Commit

Permalink
solana: pattern match instead of label (#3361)
Browse files Browse the repository at this point in the history
* solana: pattern match instead of label

* solana: make SigVerifyParameters canonical (#3362)

all messages are checked to be the same, so we just store one of them
  • Loading branch information
kcsongor authored and a5-pickle committed Nov 7, 2023
1 parent 3de8243 commit 719d64a
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 23 deletions.
3 changes: 3 additions & 0 deletions solana/programs/core-bridge/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ pub enum CoreBridgeError {
#[msg("InstructionAtWrongIndex")]
InstructionAtWrongIndex = 0x702,

#[msg("EmptySigVerifyInstruction")]
EmptySigVerifyInstruction = 0x703,

#[msg("InvalidSigVerifyInstruction")]
InvalidSigVerifyInstruction = 0x704,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct SigVerifyOffsets {

/// Result of parsing Sig Verify instruction data.
struct SigVerifyParameters {
eth_pubkey: [u8; 20],
eth_pubkeys: Vec<[u8; 20]>,
message: [u8; 32],
}

Expand Down Expand Up @@ -141,7 +141,10 @@ fn verify_signatures(ctx: Context<VerifySignatures>, args: VerifySignaturesArgs)
// NOTE: To avoid a redundant instructions sysvar check, we allow the deprecated method to
// load the instruction data.
#[allow(deprecated)]
let sig_verify_params = sysvar::instructions::load_instruction_at(
let SigVerifyParameters {
eth_pubkeys: signers,
message,
} = sysvar::instructions::load_instruction_at(
usize::from(sig_verify_index),
&instruction_sysvar_data,
)
Expand All @@ -151,13 +154,13 @@ fn verify_signatures(ctx: Context<VerifySignatures>, args: VerifySignaturesArgs)
// Number of specified signers must equal the number of signatures verified in the Sig Verify
// native program instruction.
require_eq!(
sig_verify_params.len(),
signers.len(),
guardian_indices.len(),
CoreBridgeError::SignerIndicesMismatch
);

// We use this message hash later on.
let message_hash = MessageHash::from(sig_verify_params[0].message);
let message_hash = MessageHash::from(message);
let signature_set = &mut ctx.accounts.signature_set;

// If the signature set account has not been initialized yet, establish the expected account
Expand Down Expand Up @@ -196,7 +199,7 @@ fn verify_signatures(ctx: Context<VerifySignatures>, args: VerifySignaturesArgs)
// Attempt to write `true` to represent verified guardian eth pubkey.
for (i, &signer_index) in guardian_indices.iter().enumerate() {
require!(
sig_verify_params[i].eth_pubkey == guardians[signer_index],
signers[i] == guardians[signer_index],
CoreBridgeError::InvalidGuardianKeyRecovery
);

Expand All @@ -214,7 +217,7 @@ fn verify_signatures(ctx: Context<VerifySignatures>, args: VerifySignaturesArgs)
fn deserialize_secp256k1_ix(
sig_verify_index: u16,
ix: &solana_program::instruction::Instruction,
) -> Result<Vec<SigVerifyParameters>> {
) -> Result<SigVerifyParameters> {
// Check that the program invoked is the secp256k1 program.
require_keys_eq!(
ix.program_id,
Expand All @@ -224,48 +227,58 @@ fn deserialize_secp256k1_ix(

let ix_data = &ix.data;

// First byte encodes the number of signatures.
let mut params = Vec::with_capacity(ix_data[0].into());
// The first byte encodes the number of signatures.
let num_signatures: usize = ix_data[0].into();

let mut eth_pubkeys = Vec::with_capacity(num_signatures);

// For each offset encoded, grab each SigVerify parameter (signature, eth pubkey, message).
let mut expected_message_offset = None;
for i in 0..params.capacity() {
for i in 0..num_signatures {
let offsets_idx = 1 + i * SigVerifyOffsets::INIT_SPACE;
let offsets = SigVerifyOffsets::deserialize(
let SigVerifyOffsets {
signature_offset: _,
signature_ix_index,
eth_pubkey_offset,
eth_pubkey_ix_index,
message_offset,
message_size,
message_ix_index,
} = SigVerifyOffsets::deserialize(
&mut &ix_data[offsets_idx..(offsets_idx + SigVerifyOffsets::INIT_SPACE)],
)?;
// Because guardians sign the hash of the message body hash, this verified message must be
// 32 bytes.
require_eq!(
offsets.message_size,
message_size,
32,
CoreBridgeError::InvalidSigVerifyInstruction
);

// The instruction index must be the same for signature, eth pubkey and message.
require_eq!(
u16::from(offsets.signature_ix_index),
u16::from(signature_ix_index),
sig_verify_index,
CoreBridgeError::InvalidSigVerifyInstruction
);
require_eq!(
u16::from(offsets.eth_pubkey_ix_index),
u16::from(eth_pubkey_ix_index),
sig_verify_index,
CoreBridgeError::InvalidSigVerifyInstruction
);
require_eq!(
u16::from(offsets.message_ix_index),
u16::from(message_ix_index),
sig_verify_index,
CoreBridgeError::InvalidSigVerifyInstruction
);

let eth_pubkey_offset = usize::from(offsets.eth_pubkey_offset);
let eth_pubkey_offset = usize::from(eth_pubkey_offset);
let mut eth_pubkey = [0; 20];
eth_pubkey.copy_from_slice(&ix_data[eth_pubkey_offset..(eth_pubkey_offset + 20)]);

// The message offset should be the same for each sig verify offsets since each signature is
// for the same message.
let message_offset = usize::from(offsets.message_offset);
let message_offset = usize::from(message_offset);
if let Some(expected_message_offset) = expected_message_offset {
require_eq!(
message_offset,
Expand All @@ -274,16 +287,19 @@ fn deserialize_secp256k1_ix(
);
}

eth_pubkeys.push(eth_pubkey);
expected_message_offset = Some(message_offset);
}

if let Some(message_offset) = expected_message_offset {
let mut message = [0; 32];
message.copy_from_slice(&ix_data[message_offset..(message_offset + 32)]);

params.push(SigVerifyParameters {
eth_pubkey,
Ok(SigVerifyParameters {
eth_pubkeys,
message,
});
expected_message_offset = Some(message_offset);
})
} else {
Err(CoreBridgeError::EmptySigVerifyInstruction.into())
}

// Done.
Ok(params)
}

0 comments on commit 719d64a

Please sign in to comment.