Skip to content

Commit 5a1667f

Browse files
authored
feat(spl): cheatcode for multisig (#910)
1. Unpack/Pack function recognition - Added Multisig::unpack_from_slice and Multisig::pack_into_slice to the SPL function predicates 2. Buffer size calculation - Added a #maybeDynamicSize rule for Multisig data buffers (355 bytes), matching the SPL Token Multisig::LEN constant 3. New cheatcode rule - [cheatcode-is-spl-multisig] that: - Intercepts calls to cheatcode_is_spl_multisig - Creates a symbolic SPLDataBuffer with the Multisig structure: - m: u8 - number of signers required - n: u8 - number of valid signers - is_initialized: bool - signers: [Pubkey; 11] - array of 11 signer public keys - Initializes RefCell borrow metadata with correct buffer size (355) - Adds appropriate ensures clauses constraining symbolic values
1 parent dc06fde commit 5a1667f

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

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

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ module KMIR-SPL-TOKEN
130130
// spl-token rent
131131
rule #isSPLUnpackFunc("bincode::deserialize::<'_, solana_rent::Rent>") => true
132132
rule #isSPLUnpackFunc("Rent::unpack") => true
133+
// spl-token multisig
134+
rule #isSPLUnpackFunc("<state::Multisig as solana_program_pack::Pack>::unpack_from_slice") => true
135+
rule #isSPLUnpackFunc("Multisig::unpack_from_slice") => true
133136
134137
syntax Bool ::= #isSPLPackFunc ( String ) [function, total]
135138
rule #isSPLPackFunc(_) => false [owise]
@@ -139,6 +142,9 @@ module KMIR-SPL-TOKEN
139142
// spl-token mint
140143
rule #isSPLPackFunc("<state::Mint as solana_program_pack::Pack>::pack_into_slice") => true
141144
rule #isSPLPackFunc("Mint::pack_into_slice") => true
145+
// spl-token multisig
146+
rule #isSPLPackFunc("<state::Multisig as solana_program_pack::Pack>::pack_into_slice") => true
147+
rule #isSPLPackFunc("Multisig::pack_into_slice") => true
142148
143149
syntax Bool ::= #isSPLRentGetFunc ( String ) [function, total]
144150
rule #isSPLRentGetFunc(_) => false [owise]
@@ -198,6 +204,25 @@ module KMIR-SPL-TOKEN
198204
)
199205
=> dynamicSize(17)
200206
[priority(30)]
207+
208+
// Multisig data buffer length (Multisig::LEN = 355)
209+
rule #maybeDynamicSize(
210+
dynamicSize(_),
211+
SPLDataBuffer(
212+
Aggregate(variantIdx(0),
213+
ListItem(Integer(_, 8, false)) // m
214+
ListItem(Integer(_, 8, false)) // n
215+
ListItem(BoolVal(_)) // is_initialized
216+
ListItem(Range( // signers: [Pubkey; 11]
217+
ListItem(_) ListItem(_) ListItem(_) ListItem(_) ListItem(_)
218+
ListItem(_) ListItem(_) ListItem(_) ListItem(_) ListItem(_)
219+
ListItem(_)
220+
))
221+
)
222+
)
223+
)
224+
=> dynamicSize(355)
225+
[priority(30)]
201226
```
202227

203228
## Cheatcode handling
@@ -409,6 +434,54 @@ The `#initBorrow` helper resets borrow counters to 0 and sets the correct dynami
409434
ensures 0 <=Int ?SplRentLamportsPerByteYear andBool ?SplRentLamportsPerByteYear <Int (1 <<Int 32)
410435
andBool 0 <=Int ?SplRentBurnPercent andBool ?SplRentBurnPercent <=Int 100
411436
[priority(30), preserves-definedness]
437+
438+
rule [cheatcode-is-spl-multisig]:
439+
<k> #execTerminatorCall(_, FUNC, operandCopy(place(LOCAL, PROJS)) .Operands, _DEST, TARGET, _UNWIND) ~> _CONT
440+
=> #forceSetPlaceValue(
441+
place(LOCAL, appendP(PROJS, DATA_BUFFER_PROJS)), // navigate to [u8] data buffer
442+
SPLDataBuffer(
443+
Aggregate(variantIdx(0),
444+
ListItem(Integer(?SplMultisigM:Int, 8, false)) // m: u8
445+
ListItem(Integer(?SplMultisigN:Int, 8, false)) // n: u8
446+
ListItem(BoolVal(?_SplMultisigInitialised:Bool)) // is_initialized: bool
447+
ListItem(Range( // signers: [Pubkey; 11]
448+
ListItem(?SplSigner0:List)
449+
ListItem(?SplSigner1:List)
450+
ListItem(?SplSigner2:List)
451+
ListItem(?SplSigner3:List)
452+
ListItem(?SplSigner4:List)
453+
ListItem(?SplSigner5:List)
454+
ListItem(?SplSigner6:List)
455+
ListItem(?SplSigner7:List)
456+
ListItem(?SplSigner8:List)
457+
ListItem(?SplSigner9:List)
458+
ListItem(?SplSigner10:List)
459+
))
460+
)
461+
)
462+
)
463+
~> #forceSetPlaceValue(
464+
place(LOCAL, appendP(PROJS, REFCELL_PROJS)), // navigate to RefCell for borrow init
465+
#initBorrow(operandCopy(place(LOCAL, appendP(PROJS, REFCELL_PROJS))), 355)
466+
)
467+
~> #continueAt(TARGET)
468+
</k>
469+
requires #functionName(FUNC) ==String "spl_token::entrypoint::cheatcode_is_spl_multisig"
470+
orBool #functionName(FUNC) ==String "cheatcode_is_spl_multisig"
471+
ensures 0 <=Int ?SplMultisigM andBool ?SplMultisigM <Int 256
472+
andBool 0 <=Int ?SplMultisigN andBool ?SplMultisigN <Int 256
473+
andBool #isSplPubkey(?SplSigner0)
474+
andBool #isSplPubkey(?SplSigner1)
475+
andBool #isSplPubkey(?SplSigner2)
476+
andBool #isSplPubkey(?SplSigner3)
477+
andBool #isSplPubkey(?SplSigner4)
478+
andBool #isSplPubkey(?SplSigner5)
479+
andBool #isSplPubkey(?SplSigner6)
480+
andBool #isSplPubkey(?SplSigner7)
481+
andBool #isSplPubkey(?SplSigner8)
482+
andBool #isSplPubkey(?SplSigner9)
483+
andBool #isSplPubkey(?SplSigner10)
484+
[priority(30), preserves-definedness]
412485
```
413486

414487
## RefCell borrow helpers

0 commit comments

Comments
 (0)