Skip to content

Commit f34ff62

Browse files
Tyera Eulbergaeyakovenkomvines
authored andcommitted
Prep for spl-token v3.5.0 (#3426)
* check that unpack is tolerant of small sizes (#3416) * 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]> * Readd proptests without losing unit test, #3421 Co-authored-by: anatoly yakovenko <[email protected]> Co-authored-by: Michael Vines <[email protected]>
1 parent 49fb7ab commit f34ff62

File tree

1 file changed

+44
-39
lines changed

1 file changed

+44
-39
lines changed

program/src/instruction.rs

Lines changed: 44 additions & 39 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,47 +521,19 @@ impl<'a> TokenInstruction<'a> {
519521
10 => Self::FreezeAccount,
520522
11 => Self::ThawAccount,
521523
12 => {
522-
let (amount, rest) = rest.split_at(8);
523-
let amount = amount
524-
.try_into()
525-
.ok()
526-
.map(u64::from_le_bytes)
527-
.ok_or(InvalidInstruction)?;
528-
let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?;
529-
524+
let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
530525
Self::TransferChecked { amount, decimals }
531526
}
532527
13 => {
533-
let (amount, rest) = rest.split_at(8);
534-
let amount = amount
535-
.try_into()
536-
.ok()
537-
.map(u64::from_le_bytes)
538-
.ok_or(InvalidInstruction)?;
539-
let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?;
540-
528+
let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
541529
Self::ApproveChecked { amount, decimals }
542530
}
543531
14 => {
544-
let (amount, rest) = rest.split_at(8);
545-
let amount = amount
546-
.try_into()
547-
.ok()
548-
.map(u64::from_le_bytes)
549-
.ok_or(InvalidInstruction)?;
550-
let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?;
551-
532+
let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
552533
Self::MintToChecked { amount, decimals }
553534
}
554535
15 => {
555-
let (amount, rest) = rest.split_at(8);
556-
let amount = amount
557-
.try_into()
558-
.ok()
559-
.map(u64::from_le_bytes)
560-
.ok_or(InvalidInstruction)?;
561-
let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?;
562-
536+
let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?;
563537
Self::BurnChecked { amount, decimals }
564538
}
565539
16 => {
@@ -588,12 +562,7 @@ impl<'a> TokenInstruction<'a> {
588562
21 => Self::GetAccountDataSize,
589563
22 => Self::InitializeImmutableOwner,
590564
23 => {
591-
let (amount, _rest) = rest.split_at(8);
592-
let amount = amount
593-
.try_into()
594-
.ok()
595-
.map(u64::from_le_bytes)
596-
.ok_or(InvalidInstruction)?;
565+
let (amount, _rest) = Self::unpack_u64(rest)?;
597566
Self::AmountToUiAmount { amount }
598567
}
599568
24 => {
@@ -745,6 +714,21 @@ impl<'a> TokenInstruction<'a> {
745714
COption::None => buf.push(0),
746715
}
747716
}
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+
}
748732
}
749733

750734
/// Specifies the authority type for SetAuthority instructions
@@ -1447,7 +1431,7 @@ pub fn is_valid_signer_index(index: usize) -> bool {
14471431

14481432
#[cfg(test)]
14491433
mod test {
1450-
use super::*;
1434+
use {super::*, proptest::prelude::*};
14511435

14521436
#[test]
14531437
fn test_instruction_packing() {
@@ -1689,4 +1673,25 @@ mod test {
16891673
let unpacked = TokenInstruction::unpack(&expect).unwrap();
16901674
assert_eq!(unpacked, check);
16911675
}
1676+
1677+
#[test]
1678+
fn test_instruction_unpack_panic() {
1679+
for i in 0..255u8 {
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+
}
1685+
}
1686+
}
1687+
1688+
proptest! {
1689+
#![proptest_config(ProptestConfig::with_cases(1024))]
1690+
#[test]
1691+
fn test_instruction_unpack_proptest(
1692+
data in prop::collection::vec(any::<u8>(), 0..255)
1693+
) {
1694+
let _no_panic = TokenInstruction::unpack(&data);
1695+
}
1696+
}
16921697
}

0 commit comments

Comments
 (0)