Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit c618de3

Browse files
Tyera Eulbergmvines
andauthored
Refactor unpack and make test more robust (#3417)
* Refactor hasty fix to match token-2022 * Make test exhaustive * cargo fmt Co-authored-by: Michael Vines <[email protected]>
1 parent e0ff9ad commit c618de3

File tree

1 file changed

+27
-55
lines changed

1 file changed

+27
-55
lines changed

token/program/src/instruction.rs

Lines changed: 27 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use std::mem::size_of;
1515
pub const MIN_SIGNERS: usize = 1;
1616
/// Maximum number of multisignature signers (max N)
1717
pub const MAX_SIGNERS: usize = 11;
18+
/// Serialized length of a u64, for unpacking
19+
const U64_BYTES: usize = 8;
1820

1921
/// Instructions supported by the token program.
2022
#[repr(C)]
@@ -519,59 +521,19 @@ impl<'a> TokenInstruction<'a> {
519521
10 => Self::FreezeAccount,
520522
11 => Self::ThawAccount,
521523
12 => {
522-
if rest.len() < 8 {
523-
return Err(TokenError::InvalidInstruction.into());
524-
}
525-
let (amount, rest) = rest.split_at(8);
526-
let amount = amount
527-
.try_into()
528-
.ok()
529-
.map(u64::from_le_bytes)
530-
.ok_or(InvalidInstruction)?;
531-
let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?;
532-
524+
let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
533525
Self::TransferChecked { amount, decimals }
534526
}
535527
13 => {
536-
if rest.len() < 8 {
537-
return Err(TokenError::InvalidInstruction.into());
538-
}
539-
let (amount, rest) = rest.split_at(8);
540-
let amount = amount
541-
.try_into()
542-
.ok()
543-
.map(u64::from_le_bytes)
544-
.ok_or(InvalidInstruction)?;
545-
let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?;
546-
528+
let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
547529
Self::ApproveChecked { amount, decimals }
548530
}
549531
14 => {
550-
if rest.len() < 8 {
551-
return Err(TokenError::InvalidInstruction.into());
552-
}
553-
let (amount, rest) = rest.split_at(8);
554-
let amount = amount
555-
.try_into()
556-
.ok()
557-
.map(u64::from_le_bytes)
558-
.ok_or(InvalidInstruction)?;
559-
let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?;
560-
532+
let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
561533
Self::MintToChecked { amount, decimals }
562534
}
563535
15 => {
564-
if rest.len() < 8 {
565-
return Err(TokenError::InvalidInstruction.into());
566-
}
567-
let (amount, rest) = rest.split_at(8);
568-
let amount = amount
569-
.try_into()
570-
.ok()
571-
.map(u64::from_le_bytes)
572-
.ok_or(InvalidInstruction)?;
573-
let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?;
574-
536+
let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
575537
Self::BurnChecked { amount, decimals }
576538
}
577539
16 => {
@@ -600,15 +562,7 @@ impl<'a> TokenInstruction<'a> {
600562
21 => Self::GetAccountDataSize,
601563
22 => Self::InitializeImmutableOwner,
602564
23 => {
603-
if rest.len() < 8 {
604-
return Err(TokenError::InvalidInstruction.into());
605-
}
606-
let (amount, _rest) = rest.split_at(8);
607-
let amount = amount
608-
.try_into()
609-
.ok()
610-
.map(u64::from_le_bytes)
611-
.ok_or(InvalidInstruction)?;
565+
let (amount, _rest) = Self::unpack_u64(rest)?;
612566
Self::AmountToUiAmount { amount }
613567
}
614568
24 => {
@@ -760,6 +714,21 @@ impl<'a> TokenInstruction<'a> {
760714
COption::None => buf.push(0),
761715
}
762716
}
717+
718+
fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> {
719+
let value = input
720+
.get(..U64_BYTES)
721+
.and_then(|slice| slice.try_into().ok())
722+
.map(u64::from_le_bytes)
723+
.ok_or(TokenError::InvalidInstruction)?;
724+
Ok((value, &input[U64_BYTES..]))
725+
}
726+
727+
fn unpack_amount_decimals(input: &[u8]) -> Result<(u64, u8, &[u8]), ProgramError> {
728+
let (amount, rest) = Self::unpack_u64(input)?;
729+
let (&decimals, rest) = rest.split_first().ok_or(TokenError::InvalidInstruction)?;
730+
Ok((amount, decimals, rest))
731+
}
763732
}
764733

765734
/// Specifies the authority type for SetAuthority instructions
@@ -1708,8 +1677,11 @@ mod test {
17081677
#[test]
17091678
fn test_instruction_unpack_panic() {
17101679
for i in 0..255u8 {
1711-
let expect = Vec::from([i, 1, 0, 0, 0, 0, 0, 0, 0, 2]);
1712-
_ = TokenInstruction::unpack(&expect[0..2]);
1680+
for j in 1..10 {
1681+
let mut data = vec![0; j];
1682+
data[0] = i;
1683+
let _no_panic = TokenInstruction::unpack(&data);
1684+
}
17131685
}
17141686
}
17151687
}

0 commit comments

Comments
 (0)