Skip to content

Commit db9cc3f

Browse files
authored
Added Spl support for cheatcode_maybe_same_account (#927)
This cheatcode will retrieve two `AccountInfo`s and their corresponding `SplDataBuffer`s. If the `AccountInfo`s have the same key, then it will link all the fields between the `SplDataBuffer`s to be equal. If the keys are different then do nothing.
1 parent 1e382ee commit db9cc3f

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

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

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,94 @@ Since float casts create thunks, we simplify this pattern directly to `PRODUCT *
624624
=> Integer(PRODUCT *Int 2, 64, false)
625625
```
626626

627+
## Linking identical accounts
628+
629+
When the `AccountInfo` are provided to the program from the solana runtime,
630+
we restrict that if they have the same `key` then the rest of their fields are
631+
the same. This cheatcode should only be on two `AccountInfo` after `cheatcode_is_account`
632+
is called on those `AccountInfo` to set up the symbolic state. Furthermore this should
633+
be called prior to capturing initial state and prior to executing the implementation.
634+
635+
```{.k .symbolic}
636+
// Path to account key: &AccountInfo -> AccountInfo -> key -> &Pubkey -> Pubkey
637+
syntax ProjectionElems ::= "KEY_PROJS" [alias]
638+
rule KEY_PROJS => projectionElemDeref // deref &AccountInfo
639+
projectionElemField(fieldIdx(0), #hack()) // .key (&Pubkey)
640+
projectionElemDeref // deref to Pubkey
641+
.ProjectionElems
642+
643+
// Cheatcode to link two accounts if they have the same key
644+
// Usage: cheatcode_maybe_same_account(&account1, &account2)
645+
// Effect: If account1.key == account2.key, then all SPL data fields are constrained equal
646+
rule [cheatcode-maybe-same-account]:
647+
<k> #execTerminatorCall(_, FUNC,
648+
operandCopy(place(LOCAL1, PROJS1))
649+
operandCopy(place(LOCAL2, PROJS2))
650+
.Operands, _DEST, TARGET, _UNWIND) ~> _CONT
651+
=> #maybeLinkAccounts(
652+
operandCopy(place(LOCAL1, appendP(PROJS1, KEY_PROJS))),
653+
operandCopy(place(LOCAL2, appendP(PROJS2, KEY_PROJS))),
654+
operandCopy(place(LOCAL1, appendP(PROJS1, DATA_BUFFER_PROJS))),
655+
operandCopy(place(LOCAL2, appendP(PROJS2, DATA_BUFFER_PROJS)))
656+
) ~> #continueAt(TARGET)
657+
</k>
658+
requires #functionName(FUNC) ==String "cheatcode_maybe_same_account"
659+
orBool #functionName(FUNC) ==String "spl_token::entrypoint::cheatcode_maybe_same_account"
660+
[priority(30), preserves-definedness]
661+
662+
// Helper to evaluate keys and data, then apply constraint
663+
syntax KItem ::= #maybeLinkAccounts(Evaluation, Evaluation, Evaluation, Evaluation) [seqstrict]
664+
665+
// Case: keys are equal - add ensures clause to constrain SPL data equality
666+
// The ensures clause adds the constraint that all SPL fields must be equal
667+
rule <k> #maybeLinkAccounts(
668+
Aggregate(variantIdx(0), ListItem(Range(KEY1))),
669+
Aggregate(variantIdx(0), ListItem(Range(KEY2))),
670+
SPLDataBuffer(Aggregate(variantIdx(0),
671+
ListItem(Aggregate(variantIdx(0), ListItem(Range(MINT1))))
672+
ListItem(Aggregate(variantIdx(0), ListItem(Range(OWNER1))))
673+
ListItem(Integer(AMOUNT1, 64, false))
674+
ListItem(Aggregate(variantIdx(HAS_DELEG1), ListItem(Aggregate(variantIdx(0), ListItem(Range(DELEG1))))))
675+
ListItem(Aggregate(variantIdx(STATE1), .List))
676+
ListItem(Aggregate(variantIdx(HAS_NATIVE1), ListItem(Integer(NATIVE1, 64, false))))
677+
ListItem(Integer(DELEG_AMT1, 64, false))
678+
ListItem(Aggregate(variantIdx(HAS_CLOSE1), ListItem(Aggregate(variantIdx(0), ListItem(Range(CLOSE1))))))
679+
)),
680+
SPLDataBuffer(Aggregate(variantIdx(0),
681+
ListItem(Aggregate(variantIdx(0), ListItem(Range(MINT2))))
682+
ListItem(Aggregate(variantIdx(0), ListItem(Range(OWNER2))))
683+
ListItem(Integer(AMOUNT2, 64, false))
684+
ListItem(Aggregate(variantIdx(HAS_DELEG2), ListItem(Aggregate(variantIdx(0), ListItem(Range(DELEG2))))))
685+
ListItem(Aggregate(variantIdx(STATE2), .List))
686+
ListItem(Aggregate(variantIdx(HAS_NATIVE2), ListItem(Integer(NATIVE2, 64, false))))
687+
ListItem(Integer(DELEG_AMT2, 64, false))
688+
ListItem(Aggregate(variantIdx(HAS_CLOSE2), ListItem(Aggregate(variantIdx(0), ListItem(Range(CLOSE2))))))
689+
))
690+
) => .K ... </k>
691+
requires KEY1 ==K KEY2
692+
ensures MINT1 ==K MINT2
693+
andBool OWNER1 ==K OWNER2
694+
andBool AMOUNT1 ==Int AMOUNT2
695+
andBool HAS_DELEG1 ==Int HAS_DELEG2
696+
andBool DELEG1 ==K DELEG2
697+
andBool STATE1 ==Int STATE2
698+
andBool HAS_NATIVE1 ==Int HAS_NATIVE2
699+
andBool NATIVE1 ==Int NATIVE2
700+
andBool DELEG_AMT1 ==Int DELEG_AMT2
701+
andBool HAS_CLOSE1 ==Int HAS_CLOSE2
702+
andBool CLOSE1 ==K CLOSE2
703+
[priority(30)]
704+
705+
// Case: keys are different - no constraint needed
706+
rule <k> #maybeLinkAccounts(
707+
Aggregate(variantIdx(0), ListItem(Range(KEY1))),
708+
Aggregate(variantIdx(0), ListItem(Range(KEY2))),
709+
_, _
710+
) => .K ... </k>
711+
requires notBool (KEY1 ==K KEY2)
712+
[priority(30)]
713+
```
714+
627715
```k
628716
endmodule
629717
```

0 commit comments

Comments
 (0)