Skip to content

Commit 89c2ff0

Browse files
authored
feat(spl): provide cheatcodes for unpack_from_slice and pack_into_slice (#881)
2 parents 6ae3250 + 712b95a commit 89c2ff0

File tree

3 files changed

+62
-58
lines changed

3 files changed

+62
-58
lines changed

kmir/src/kmir/kdist/mir-semantics/symbolic/spl-token.md

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ embedded.
1616

1717
```
1818
cheatcode_is_spl_account(acc)
19-
Account::unpack_unchecked(&acc.data.borrow())
20-
Account::pack(a, &mut acc.data.borrow_mut())
19+
Account::unpack_from_slice(&acc.data.borrow())
20+
Account::pack_into_slice(&a, &mut acc.data.borrow_mut())
2121
-> #isSPLRcRefCellDerefFunc (rule [spl-rc-deref])
2222
-> #isSPLBorrowFunc (rule [spl-borrow-data])
2323
-> #isSPLRefDerefFunc (rule [spl-ref-deref])
@@ -148,26 +148,20 @@ module KMIR-SPL-TOKEN
148148
syntax Bool ::= #isSPLUnpackFunc ( String ) [function, total]
149149
rule #isSPLUnpackFunc(_) => false [owise]
150150
// spl-token account
151-
rule #isSPLUnpackFunc("solana_program_pack::<spl_token_interface::state::Account as solana_program_pack::Pack>::unpack_unchecked") => true
152-
rule #isSPLUnpackFunc("solana_program_pack::<spl_token_interface::state::Account as solana_program_pack::Pack>::unpack") => true
153-
// mock account
154-
rule #isSPLUnpackFunc("Account::unpack_unchecked") => true
151+
rule #isSPLUnpackFunc("<state::Account as solana_program_pack::Pack>::unpack_from_slice") => true
152+
rule #isSPLUnpackFunc("Account::unpack_from_slice") => true
155153
// spl-token mint
156-
rule #isSPLUnpackFunc("solana_program_pack::<spl_token_interface::state::Mint as solana_program_pack::Pack>::unpack_unchecked") => true
157-
rule #isSPLUnpackFunc("solana_program_pack::<spl_token_interface::state::Mint as solana_program_pack::Pack>::unpack") => true
158-
// mock mint
159-
rule #isSPLUnpackFunc("Mint::unpack_unchecked") => true
154+
rule #isSPLUnpackFunc("<state::Mint as solana_program_pack::Pack>::unpack_from_slice") => true
155+
rule #isSPLUnpackFunc("Mint::unpack_from_slice") => true
160156
161157
syntax Bool ::= #isSPLPackFunc ( String ) [function, total]
162158
rule #isSPLPackFunc(_) => false [owise]
163159
// spl-token account
164-
rule #isSPLPackFunc("solana_program_pack::<spl_token_interface::state::Account as solana_program_pack::Pack>::pack") => true
165-
// mock account
166-
rule #isSPLPackFunc("Account::pack") => true
160+
rule #isSPLPackFunc("<state::Account as solana_program_pack::Pack>::pack_into_slice") => true
161+
rule #isSPLPackFunc("Account::pack_into_slice") => true
167162
// spl-token mint
168-
rule #isSPLPackFunc("solana_program_pack::<spl_token_interface::state::Mint as solana_program_pack::Pack>::pack") => true
169-
// mock mint
170-
rule #isSPLPackFunc("Mint::pack") => true
163+
rule #isSPLPackFunc("<state::Mint as solana_program_pack::Pack>::pack_into_slice") => true
164+
rule #isSPLPackFunc("Mint::pack_into_slice") => true
171165
172166
// Rent sysvar calls (includes mock harness direct calls to Rent::from_account_info / Rent::get)
173167
syntax Bool ::= #isSPLRentFromAccountInfoFunc ( String ) [function, total]
@@ -767,7 +761,7 @@ expose the wrapped payload directly.
767761
[owise]
768762
```
769763

770-
## Account::unpack / Account::pack
764+
## Account::unpack_from_slice / Account::pack_into_slice
771765
```k
772766
rule [spl-account-unpack]:
773767
<k> #execTerminator(terminator(terminatorKindCall(FUNC, OP:Operand .Operands, DEST, someBasicBlockIdx(TARGET), _UNWIND), _SPAN))
@@ -815,22 +809,21 @@ expose the wrapped payload directly.
815809

816810
```k
817811
rule [spl-account-pack]:
818-
<k> #execTerminator(terminator(terminatorKindCall(FUNC, SRC_OP:Operand BUF_OP:Operand .Operands, DEST, someBasicBlockIdx(TARGET), _UNWIND), _SPAN))
819-
=> #mkSPLAccountPack(SRC_OP, BUF_OP, DEST)
812+
<k> #execTerminator(terminator(terminatorKindCall(FUNC, SRC_OP:Operand BUF_OP:Operand .Operands, _DEST, someBasicBlockIdx(TARGET), _UNWIND), _SPAN))
813+
=> #mkSPLAccountPack(SRC_OP, BUF_OP)
820814
~> #execBlockIdx(TARGET)
821815
...
822816
</k>
823817
requires #isSPLPackFunc(#functionName(lookupFunction(#tyOfCall(FUNC))))
824818
[priority(30), preserves-definedness]
825819
826-
syntax KItem ::= #mkSPLAccountPack ( Evaluation , Evaluation , Place ) [seqstrict(1,2)]
820+
syntax KItem ::= #mkSPLAccountPack ( Evaluation , Evaluation ) [seqstrict(1,2)]
827821
828-
rule <k> #mkSPLAccountPack(ACCOUNT, SPLDataBorrowMut(PLACE, SPLDataBuffer(_)), DEST)
822+
rule <k> #mkSPLAccountPack(ACCOUNT, SPLDataBorrowMut(PLACE, SPLDataBuffer(_)))
829823
=> #forceSetPlaceValue(
830824
PLACE,
831825
SPLRefCell(PLACE, SPLDataBuffer(ACCOUNT))
832826
)
833-
~> #setLocalValue(DEST, Aggregate(variantIdx(0), ListItem(Aggregate(variantIdx(0), .List))))
834827
...
835828
</k>
836829
```

kmir/src/tests/integration/data/prove-rs/spl_token_domain_data.rs renamed to kmir/src/tests/integration/data/rs/spl_token_domain_data.rs

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ impl Account {
318318
matches!(self.is_native, COption::Some(_))
319319
}
320320

321-
fn unpack_unchecked(data: &[u8]) -> Result<Self, ProgramError> {
321+
fn unpack_from_slice(data: &[u8]) -> Result<Self, ProgramError> {
322322
if data.len() < Self::LEN {
323323
return Err(ProgramError::InvalidAccountData);
324324
}
@@ -348,18 +348,26 @@ impl Account {
348348
})
349349
}
350350

351+
fn unpack_unchecked(data: &[u8]) -> Result<Self, ProgramError> {
352+
Self::unpack_from_slice(data)
353+
}
354+
355+
fn pack_into_slice(self, dst: &mut [u8]) {
356+
dst[0..32].copy_from_slice(self.mint.as_ref());
357+
dst[32..64].copy_from_slice(self.owner.as_ref());
358+
dst[64..72].copy_from_slice(&self.amount.to_le_bytes());
359+
encode_coption_key(&self.delegate, &mut dst[72..108]);
360+
dst[108] = self.state as u8;
361+
encode_coption_u64(&self.is_native, &mut dst[109..121]);
362+
dst[121..129].copy_from_slice(&self.delegated_amount.to_le_bytes());
363+
encode_coption_key(&self.close_authority, &mut dst[129..165]);
364+
}
365+
351366
fn pack(account: Account, dst: &mut [u8]) -> Result<(), ProgramError> {
352367
if dst.len() < Self::LEN {
353368
return Err(ProgramError::InvalidAccountData);
354369
}
355-
dst[0..32].copy_from_slice(account.mint.as_ref());
356-
dst[32..64].copy_from_slice(account.owner.as_ref());
357-
dst[64..72].copy_from_slice(&account.amount.to_le_bytes());
358-
encode_coption_key(&account.delegate, &mut dst[72..108]);
359-
dst[108] = account.state as u8;
360-
encode_coption_u64(&account.is_native, &mut dst[109..121]);
361-
dst[121..129].copy_from_slice(&account.delegated_amount.to_le_bytes());
362-
encode_coption_key(&account.close_authority, &mut dst[129..165]);
370+
account.pack_into_slice(dst);
363371
Ok(())
364372
}
365373
}
@@ -388,7 +396,7 @@ impl Default for Mint {
388396
impl Mint {
389397
const LEN: usize = 82;
390398

391-
fn unpack_unchecked(data: &[u8]) -> Result<Self, ProgramError> {
399+
fn unpack_from_slice(data: &[u8]) -> Result<Self, ProgramError> {
392400
if data.len() < Self::LEN {
393401
return Err(ProgramError::InvalidAccountData);
394402
}
@@ -410,22 +418,23 @@ impl Mint {
410418
})
411419
}
412420

421+
fn unpack_unchecked(data: &[u8]) -> Result<Self, ProgramError> {
422+
Self::unpack_from_slice(data)
423+
}
424+
425+
fn pack_into_slice(self, dst: &mut [u8]) {
426+
encode_coption_key(&self.mint_authority, &mut dst[0..36]);
427+
dst[36..44].copy_from_slice(&self.supply.to_le_bytes());
428+
dst[44] = self.decimals;
429+
dst[45] = self.is_initialized as u8;
430+
encode_coption_key(&self.freeze_authority, &mut dst[46..82]);
431+
}
432+
413433
fn pack(mint: Mint, dst: &mut [u8]) -> Result<(), ProgramError> {
414434
if dst.len() < Self::LEN {
415435
return Err(ProgramError::InvalidAccountData);
416436
}
417-
let Mint {
418-
mint_authority,
419-
supply,
420-
decimals,
421-
is_initialized,
422-
freeze_authority,
423-
} = mint;
424-
encode_coption_key(&mint_authority, &mut dst[0..36]);
425-
dst[36..44].copy_from_slice(&supply.to_le_bytes());
426-
dst[44] = decimals;
427-
dst[45] = is_initialized as u8;
428-
encode_coption_key(&freeze_authority, &mut dst[46..82]);
437+
mint.pack_into_slice(dst);
429438
Ok(())
430439
}
431440
}
@@ -454,7 +463,7 @@ impl Default for Multisig {
454463
impl Multisig {
455464
const LEN: usize = 355;
456465

457-
fn unpack_unchecked(data: &[u8]) -> Result<Self, ProgramError> {
466+
fn unpack_from_slice(data: &[u8]) -> Result<Self, ProgramError> {
458467
if data.len() < Self::LEN {
459468
return Err(ProgramError::InvalidAccountData);
460469
}
@@ -479,17 +488,25 @@ impl Multisig {
479488
})
480489
}
481490

491+
fn unpack_unchecked(data: &[u8]) -> Result<Self, ProgramError> {
492+
Self::unpack_from_slice(data)
493+
}
494+
495+
fn pack_into_slice(self, dst: &mut [u8]) {
496+
dst[0] = self.m;
497+
dst[1] = self.n;
498+
dst[2] = self.is_initialized as u8;
499+
for (i, signer) in self.signers.iter().enumerate() {
500+
let start = 3 + i * 32;
501+
dst[start..start + 32].copy_from_slice(signer.as_ref());
502+
}
503+
}
504+
482505
fn pack(multisig: Multisig, dst: &mut [u8]) -> Result<(), ProgramError> {
483506
if dst.len() < Self::LEN {
484507
return Err(ProgramError::InvalidAccountData);
485508
}
486-
dst[0] = multisig.m;
487-
dst[1] = multisig.n;
488-
dst[2] = multisig.is_initialized as u8;
489-
for (i, signer) in multisig.signers.into_iter().enumerate() {
490-
let start = 3 + i * 32;
491-
dst[start..start + 32].copy_from_slice(signer.as_ref());
492-
}
509+
multisig.pack_into_slice(dst);
493510
Ok(())
494511
}
495512
}

kmir/src/tests/integration/test_integration.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,6 @@
3737
'assume-cheatcode': ['check_assume'],
3838
'assume-cheatcode-conflict-fail': ['check_assume_conflict'],
3939
'transmute-bytes': ['bytes_to_u64', 'u64_to_bytes'],
40-
'spl_token_domain_data': [
41-
'test_spl_account_domain_data',
42-
'test_spl_mint_domain_data',
43-
'test_spl_rent_domain_data',
44-
'test_spl_account_lamports_read_then_write',
45-
],
4640
}
4741
PROVE_RS_SHOW_SPECS = [
4842
'local-raw-fail',

0 commit comments

Comments
 (0)