diff --git a/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/buf.md b/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/buf.md index 40b09b5fa1..1ea46865b8 100644 --- a/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/buf.md +++ b/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/buf.md @@ -37,10 +37,17 @@ module BUF syntax Int ::= #powByteLen ( Int ) [symbol(#powByteLen), function, no-evaluators] // --------------------------------------------------------------------------------- // rule #powByteLen(SIZE) => 2 ^Int (8 *Int SIZE) - rule 2 ^Int (8 *Int SIZE) => #powByteLen(SIZE) [symbolic(SIZE), simplification] + rule 2 ^Int (8 *Int SIZE) => #powByteLen(SIZE) [symbolic(SIZE), simplification, preserves-definedness] rule 0 true requires 0 <=Int SIZE [simplification, preserves-definedness] rule SIZE true requires 0 <=Int SIZE [simplification, preserves-definedness] + // Concrete comparison: CONST < #powByteLen(N) evaluates when both arguments are concrete. + // Needed because 2^Int(8*N) is rewritten to #powByteLen(N) even for concrete N (via the + // symbolic(N) rule firing before path-condition lookup makes N concrete), but #powByteLen + // has no-evaluators so the result can't be computed directly. + rule [powByteLen-lt-concrete]: CONST true + requires 0 <=Int N andBool CONST WM [ IDX := #buf(1, VAL) ] [simplification] rule #bufStrict(SIZE, DATA) => #buf(SIZE, DATA) diff --git a/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm-types.md b/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm-types.md index d55b737ded..e4cabe0a65 100644 --- a/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm-types.md +++ b/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm-types.md @@ -358,8 +358,8 @@ Bytes helper functions // ------------------------------------------------------------------------- rule #asInteger(WS) => Bytes2Int(WS, BE, Unsigned) [concrete] - syntax Account ::= #asAccount ( Bytes ) [symbol(#asAccount), function] - // ---------------------------------------------------------------------------------- + syntax Account ::= #asAccount ( Bytes ) [symbol(#asAccount), function, total] + // ----------------------------------------------------------------------------- rule #asAccount(BS) => .Account requires lengthBytes(BS) ==Int 0 rule #asAccount(BS) => #asWord(BS) [owise] diff --git a/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm.md b/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm.md index aa0e14fd92..27547a6feb 100644 --- a/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm.md +++ b/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm.md @@ -1812,8 +1812,8 @@ System Transaction Configuration ``` ```k - syntax Int ::= #widthOpCode(Int) [symbol(#widthOpCode), function] - // ----------------------------------------------------------------- + syntax Int ::= #widthOpCode(Int) [symbol(#widthOpCode), function, total, smtlib(widthOpCode)] + // --------------------------------------------------------------------------------------------- rule #widthOpCode(W) => W -Int 94 requires W >=Int 96 andBool W <=Int 127 rule #widthOpCode(_) => 1 [owise] diff --git a/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/lemmas/bytes-simplification.k b/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/lemmas/bytes-simplification.k index 392a50c6e9..402e488e35 100644 --- a/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/lemmas/bytes-simplification.k +++ b/kevm-pyk/src/kevm_pyk/kproj/evm-semantics/lemmas/bytes-simplification.k @@ -29,6 +29,10 @@ module BYTES-SIMPLIFICATION [symbolic] rule [bytes-concat-empty-right]: B:Bytes +Bytes .Bytes => B [simplification] rule [bytes-concat-empty-left]: .Bytes +Bytes B:Bytes => B [simplification] + // b"" is a Kore domain value (\dv{Bytes}("")) distinct from the .Bytes constructor; + // LLVM hooks (e.g. #encodeArgs base case) return b"", not .Bytes, so we need both forms. + rule [bytes-concat-empty-right-concrete]: B:Bytes +Bytes b"" => B [simplification] + rule [bytes-concat-empty-left-concrete]: b"" +Bytes B:Bytes => B [simplification] rule [bytes-concat-right-assoc-symb-l]: (B1:Bytes +Bytes B2:Bytes) +Bytes B3:Bytes => B1 +Bytes (B2 +Bytes B3) [symbolic(B1), simplification(40)] rule [bytes-concat-right-assoc-symb-r]: (B1:Bytes +Bytes B2:Bytes) +Bytes B3:Bytes => B1 +Bytes (B2 +Bytes B3) [symbolic(B2), simplification(40)] @@ -306,5 +310,5 @@ module BYTES-SIMPLIFICATION [symbolic] rule [lookup-as-asWord]: B:Bytes [ I:Int ] => #asWord ( #range ( B, I, 1 ) ) requires 0 <=Int I andBool I X + requires #rangeUInt ( 256, X ) + [simplification] + // Reduction: bitwise right shift in terms of `#range` rule [asWord-shr]: #asWord( BA ) >>Int N => #asWord ( #range ( BA, 0, lengthBytes( BA ) -Int ( N /Int 8 ) ) ) @@ -123,6 +131,18 @@ module EVM-INT-SIMPLIFICATION // Comparison: `#asWord(B)` is certainly less than or equal to something that is not less than the max number fitting into `lengthBytes(B)` bytes rule [asWord-le]: #asWord ( B ) <=Int X:Int => true requires ( 2 ^Int (8 *Int minInt(32, lengthBytes(B))) -Int 1 ) <=Int X [simplification, preserves-definedness] + // Comparison: `#asWord(B) < X` is false when the path condition says X <= #asWord(B). + // Loop-safe with asWord-le-false: requires clauses have #asWord on the RIGHT, so + // neither rule fires on the other's requires. + rule [asWord-lt-false]: #asWord ( B:Bytes ) false requires X <=Int #asWord ( B ) [simplification] + rule [asWord-le-false]: #asWord ( B:Bytes ) <=Int X:Int => false requires X false requires X =/=Int #asWord ( B ) [simplification] + // Comparison: `#asWord` of `+Bytes` when lower bytes match, with ` #asWord ( BA1 ) Int C +Int D => A -Int C >Int D -Int B [concrete(B, D), symbolic(A, C), simplification(45)] rule A +Int B >=Int C +Int D => A -Int C >=Int D -Int B [concrete(B, D), symbolic(A, C), simplification(45)] + rule [int-eq-comm-concrete]: + N:Int ==Int X:Int => X ==Int N + [simplification(30), concrete(N), symbolic(X)] + rule A +Int B ==Int A => B ==Int 0 [simplification(40)] rule A +Int B ==Int B => A ==Int 0 [simplification(40)] rule A +Int B ==Int C +Int B => A ==Int C [simplification(40)] @@ -101,6 +105,11 @@ module INT-SIMPLIFICATION-HASKELL [symbolic] rule A -Int B A A <=Int B [simplification, symbolic(A, B)] + // Mixed range contradictions: A < B /\ C <= A where B <= C (all concrete B, C) + // Requires is purely concrete (both B and C are concrete), so no circular self-application. + rule A:Int false requires B <=Int C [simplification, concrete(B, C)] + rule C:Int <=Int A:Int andBool A:Int false requires B <=Int C [simplification, concrete(B, C)] + endmodule module INT-SIMPLIFICATION-COMMON @@ -133,6 +142,7 @@ module INT-SIMPLIFICATION-COMMON rule (A +Int B) +Int (C -Int B) => A +Int C [simplification] rule (A -Int B) -Int (C -Int B) => A -Int C [simplification] rule ((A -Int B) -Int C) +Int B => A -Int C [simplification] + rule A +Int ((B -Int A) +Int C) => B +Int C [simplification] // 5 terms // NOTE: required for `tests/specs/functional/infinite-gas-spec.k.prove` (haskell) @@ -232,6 +242,18 @@ module INT-SIMPLIFICATION-COMMON rule [maxInt-factor-left]: maxInt ( A:Int +Int B:Int, A:Int +Int C:Int ) => A +Int maxInt ( B, C ) [simplification] rule [maxInt-factor-right]: maxInt ( A:Int +Int B:Int, C:Int +Int B:Int ) => maxInt ( A, C ) +Int B [simplification] + // minInt true + requires A true + requires B <=Int D + [simplification(40)] + // ########################################################################### // inequality // ########################################################################### @@ -241,6 +263,14 @@ module INT-SIMPLIFICATION-COMMON rule A false requires 0 <=Int B [simplification] + rule A:Int <=Int A:Int => true [simplification] + + // Higher-priority: short-circuit to true directly when path condition already has 0 <=Int B. + rule A:Int <=Int A:Int +Int B:Int => true requires 0 <=Int B [simplification(40)] + rule A:Int <=Int A:Int +Int B:Int => 0 <=Int B [simplification] + + rule A:Int false [simplification] + rule 0 true requires 0 <=Int A [simplification, preserves-definedness] // inequality sign normalization @@ -256,6 +286,9 @@ module INT-SIMPLIFICATION-COMMON rule A ==Int B => false requires 0 <=Int A andBool B false requires A true requires 0 <=Int X andBool notBool (X ==Int 0) [simplification(60)] rule X <=Int maxUInt256 => X true [simplification, smt-lemma] + rule [widthOpCode-ub]: #widthOpCode(_:Int) <=Int 33 => true [simplification, smt-lemma] + // ######################## // Boolean Logic // ######################## @@ -259,4 +270,9 @@ module LEMMAS-HASKELL [symbolic] rule [notBool-or]: notBool ( A orBool B ) => ( notBool A ) andBool ( notBool B ) [simplification(60)] rule [notBool-and]: notBool ( A andBool B ) => ( notBool A ) orBool ( notBool B ) [simplification(60)] + rule [bool-or-not-self]: B:Bool orBool notBool B:Bool => true [simplification] + rule [bool-not-or-self]: notBool B:Bool orBool B:Bool => true [simplification] + rule [bool-and-not-self]: B:Bool andBool notBool B:Bool => false [simplification] + rule [bool-not-and-self]: notBool B:Bool andBool B:Bool => false [simplification] + endmodule diff --git a/tests/failing-symbolic.haskell-booster-dev b/tests/failing-symbolic.haskell-booster-dev index 897d760720..da378e008c 100644 --- a/tests/failing-symbolic.haskell-booster-dev +++ b/tests/failing-symbolic.haskell-booster-dev @@ -59,9 +59,7 @@ tests/specs/examples/solidity-code-spec.md tests/specs/examples/storage-spec.md tests/specs/examples/sum-to-n-foundry-spec.k tests/specs/examples/sum-to-n-spec.k -tests/specs/functional/bitwise-asword-spec.k tests/specs/functional/bitwise-mask-shift-spec.k -tests/specs/functional/bitwise-simplifications-spec.k tests/specs/functional/bytes-range-spec.k tests/specs/functional/evm-int-simplifications-spec.k tests/specs/functional/infinite-gas-spec.k @@ -116,7 +114,6 @@ tests/specs/mcd-structured/cat-exhaustiveness-spec.k tests/specs/mcd-structured/cat-file-addr-pass-rough-spec.k tests/specs/mcd-structured/dai-adduu-fail-rough-spec.k tests/specs/mcd-structured/dstoken-approve-fail-rough-spec.k -tests/specs/mcd-structured/dsvalue-peek-pass-rough-spec.k tests/specs/mcd-structured/dsvalue-read-pass-summarize-spec.k tests/specs/mcd-structured/end-cash-pass-rough-spec.k tests/specs/mcd-structured/end-pack-pass-rough-spec.k diff --git a/tests/specs/functional/abi-spec.k b/tests/specs/functional/abi-spec.k index 0d27b80096..358e4a0094 100644 --- a/tests/specs/functional/abi-spec.k +++ b/tests/specs/functional/abi-spec.k @@ -3,8 +3,8 @@ requires "abi.md" module VERIFICATION imports EVM-ABI - syntax Step ::= Bool | Bytes - // ------------------------------ + syntax Step ::= Bool | Bytes | Int + // ----------------------------------- syntax KItem ::= runLemma ( Step ) [symbol(runLemma)] | doneLemma( Step ) @@ -43,22 +43,26 @@ module ABI-SPEC runLemma( #sizeOfDynamicType( #array( - #bytes(b"\x00"), - 3, + #bytes(b"\x00"), + 3, #bytes(b"\xaa"), #bytes(b"\xbb\xbb"), #bytes(b"\xcc\xcc\xcc") ) ) ==Int lengthBytes(#enc( #array( - #bytes(b"\x00"), - 3, + #bytes(b"\x00"), + 3, #bytes(b"\xaa"), #bytes(b"\xbb\xbb"), #bytes(b"\xcc\xcc\xcc") ) )) - ) - => doneLemma(true) - ... + ) + => doneLemma(true) + ... + claim [pow-byte-len-simplify]: + runLemma ( 2 ^Int (8 *Int N:Int) ) => doneLemma ( #powByteLen(N:Int) ) ... + requires 0 <=Int N + endmodule diff --git a/tests/specs/functional/int-simplifications-spec.k b/tests/specs/functional/int-simplifications-spec.k index 1516fad092..2f8e5e361b 100644 --- a/tests/specs/functional/int-simplifications-spec.k +++ b/tests/specs/functional/int-simplifications-spec.k @@ -127,4 +127,19 @@ module INT-SIMPLIFICATIONS-SPEC claim [le-maxuint256]: runLemma ( X <=Int maxUInt256 ) => doneLemma ( X + // Comparison normalization coverage (int-simplification.k [preserves-definedness]) + // --------------------------------------------------------------------------------- + + claim [int-comp-norm-add-lt]: + runLemma ( X:Int +Int 3 doneLemma ( X:Int + + claim [int-comp-norm-add-le]: + runLemma ( X:Int +Int 3 <=Int 10 ) => doneLemma ( X:Int <=Int 7 ) ... + + claim [int-comp-norm-sub-lt]: + runLemma ( X:Int -Int 3 doneLemma ( X:Int + + claim [int-comp-norm-rhs-add-lt]: + runLemma ( 7 doneLemma ( 4 + endmodule diff --git a/tests/specs/functional/lemmas-no-smt-spec.k b/tests/specs/functional/lemmas-no-smt-spec.k index dc12dc9e51..cf269ed100 100644 --- a/tests/specs/functional/lemmas-no-smt-spec.k +++ b/tests/specs/functional/lemmas-no-smt-spec.k @@ -43,4 +43,46 @@ module LEMMAS-NO-SMT-SPEC claim [bool-simpl-02]: runLemma ( bool2Word( B:Bool ) ==Int 1 ) => doneLemma ( B ==K true ) ... claim [bool-simpl-03]: runLemma ( 1 ==Int bool2Word( B:Bool ) ) => doneLemma ( B ==K true ) ... + claim [range-empty-neg-start]: + runLemma ( #range(B:Bytes, 0 -Int 1, 5) ==K .Bytes ) => doneLemma ( true ) ... + + claim [range-empty-zero-width]: + runLemma ( #range(B:Bytes, 3, 0) ==K .Bytes ) => doneLemma ( true ) ... + + // #asWord comparison — false cases + // --------------------------------- + + // asWord-lt-false: #asWord(B) < X => false when X <=Int #asWord(B) + // Here X=0, and 0 <=Int #asWord(B) is always true (asWord-lb), so no extra requires needed. + claim [asword-lt-false]: + runLemma ( #asWord(B:Bytes) doneLemma ( false ) ... + + // asWord-le-false: #asWord(B) <=Int X => false when X runLemma ( #asWord(B:Bytes) <=Int 0 ) => doneLemma ( false ) ... + requires 0 false when X =/=Int #asWord(B) + // X must be symbolic so asword-eq-num (requires concrete(X)) does not fire first. + claim [asword-eq-false]: + runLemma ( #asWord(B:Bytes) ==Int X:Int ) => doneLemma ( false ) ... + requires X =/=Int #asWord(B) + + // eq-false-lt + // ----------- + + // eq-false-lt: A ==Int B => false when A < B is in path conditions (B concrete). + claim [eq-false-lt]: + runLemma ( X:Int ==Int 42 ) => doneLemma ( false ) ... + requires X true when both concrete. + // Needed because 2^Int(8*N) is rewritten to #powByteLen(N) even for concrete N, + // but #powByteLen has no-evaluators, so the comparison cannot self-evaluate. + claim [powByteLen-lt-concrete]: + runLemma ( 31 doneLemma ( true ) ... + endmodule diff --git a/tests/specs/mcd/verification.k b/tests/specs/mcd/verification.k index cf400bac8b..2bcc3e0045 100644 --- a/tests/specs/mcd/verification.k +++ b/tests/specs/mcd/verification.k @@ -139,7 +139,7 @@ module LEMMAS-MCD [symbolic] // Keccak // ######################## - rule keccak(_) ==Int I => false requires #rangeSmall(I) [simplification] + rule keccak(_) ==Int I => false requires #rangeSmall(I) [simplification, smt-lemma] rule keccak(_) +Int I1 ==Int I2 => false requires #rangeSmall(I1) andBool #rangeSmall(I2) [simplification]