|
1 | 1 | import asyncio |
2 | 2 | import logging |
| 3 | +from time import time |
3 | 4 |
|
4 | 5 | from typing import Dict, List, Optional, Tuple, Callable |
5 | 6 |
|
@@ -1880,3 +1881,301 @@ def test_unknown_condition(self): |
1880 | 1881 | else: |
1881 | 1882 | assert npc_result.error is None |
1882 | 1883 | assert npc_result.npc_list[0].conditions == [] |
| 1884 | + |
| 1885 | + |
| 1886 | +# the tests below are malicious generator programs |
| 1887 | + |
| 1888 | +# this program: |
| 1889 | +# (mod (A B) |
| 1890 | +# (defun large_string (V N) |
| 1891 | +# (if N (large_string (concat V V) (- N 1)) V) |
| 1892 | +# ) |
| 1893 | +# (defun iter (V N) |
| 1894 | +# (if N (c V (iter V (- N 1))) ()) |
| 1895 | +# ) |
| 1896 | +# (iter (c (q . 83) (c (concat (large_string 0x00 A) (q . 100)) ())) B) |
| 1897 | +# ) |
| 1898 | +# with A=28 and B specified as {num} |
| 1899 | + |
| 1900 | +SINGLE_ARG_INT_COND = "(a (q 2 4 (c 2 (c (c (q . {opcode}) (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) ())) (c 11 ())))) (c (q (a (i 11 (q 4 5 (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 28 {num})))" # noqa |
| 1901 | + |
| 1902 | +# this program: |
| 1903 | +# (mod (A B) |
| 1904 | +# (defun large_string (V N) |
| 1905 | +# (if N (large_string (concat V V) (- N 1)) V) |
| 1906 | +# ) |
| 1907 | +# (defun iter (V N) |
| 1908 | +# (if N (c (c (q . 83) (c V ())) (iter (substr V 1) (- N 1))) ()) |
| 1909 | +# ) |
| 1910 | +# (iter (concat (large_string 0x00 A) (q . 100)) B) |
| 1911 | +# ) |
| 1912 | +# truncates the first byte of the large string being passed down for each |
| 1913 | +# iteration, in an attempt to defeat any caching of integers by node ID. |
| 1914 | +# substr is cheap, and no memory is copied, so we can perform a lot of these |
| 1915 | +SINGLE_ARG_INT_SUBSTR_COND = "(a (q 2 4 (c 2 (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c 5 ())) (a 4 (c 2 (c (substr 5 (q . 1)) (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 28 {num})))" # noqa |
| 1916 | + |
| 1917 | +# this program: |
| 1918 | +# (mod (A B) |
| 1919 | +# (defun large_string (V N) |
| 1920 | +# (if N (large_string (concat V V) (- N 1)) V) |
| 1921 | +# ) |
| 1922 | +# (defun iter (V N) |
| 1923 | +# (if N (c (c (q . 83) (c V ())) (iter (substr V 0 (- (strlen V) 1)) (- N 1))) ()) |
| 1924 | +# ) |
| 1925 | +# (iter (concat (large_string 0x00 A) (q . 0xffffffff)) B) |
| 1926 | +# ) |
| 1927 | +SINGLE_ARG_INT_SUBSTR_TAIL_COND = "(a (q 2 4 (c 2 (c (concat (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (q . {val})) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c 5 ())) (a 4 (c 2 (c (substr 5 () (- (strlen 5) (q . 1))) (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 25 {num})))" # noqa |
| 1928 | + |
| 1929 | +# (mod (A B) |
| 1930 | +# (defun large_string (V N) |
| 1931 | +# (if N (large_string (concat V V) (- N 1)) V) |
| 1932 | +# ) |
| 1933 | +# (defun iter (V N) |
| 1934 | +# (if N (c (c (q . 83) (c (concat V N) ())) (iter V (- N 1))) ()) |
| 1935 | +# ) |
| 1936 | +# (iter (large_string 0x00 A) B) |
| 1937 | +# ) |
| 1938 | +SINGLE_ARG_INT_LADDER_COND = "(a (q 2 4 (c 2 (c (a 6 (c 2 (c (q . {filler}) (c 5 ())))) (c 11 ())))) (c (q (a (i 11 (q 4 (c (q . {opcode}) (c (concat 5 11) ())) (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 2 (i 11 (q 2 6 (c 2 (c (concat 5 5) (c (- 11 (q . 1)) ())))) (q . 5)) 1) (q 24 {num})))" # noqa |
| 1939 | + |
| 1940 | +# this program: |
| 1941 | +# (mod (A B) |
| 1942 | +# (defun large_message (N) |
| 1943 | +# (lsh (q . "a") N) |
| 1944 | +# ) |
| 1945 | +# (defun iter (V N) |
| 1946 | +# (if N (c V (iter V (- N 1))) ()) |
| 1947 | +# ) |
| 1948 | +# (iter (c (q . 60) (c (large_message A) ())) B) |
| 1949 | +# ) |
| 1950 | +# with B set to {num} |
| 1951 | + |
| 1952 | +CREATE_ANNOUNCE_COND = "(a (q 2 4 (c 2 (c (c (q . {opcode}) (c (a 6 (c 2 (c 5 ()))) ())) (c 11 ())))) (c (q (a (i 11 (q 4 5 (a 4 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) 23 (q . 97) 5) (q 8184 {num})))" # noqa |
| 1953 | + |
| 1954 | +# this program: |
| 1955 | +# (mod (A) |
| 1956 | +# (defun iter (V N) |
| 1957 | +# (if N (c V (iter V (- N 1))) ()) |
| 1958 | +# ) |
| 1959 | +# (iter (q 51 "abababababababababababababababab" 1) A) |
| 1960 | +# ) |
| 1961 | +CREATE_COIN = '(a (q 2 2 (c 2 (c (q 51 "abababababababababababababababab" 1) (c 5 ())))) (c (q 2 (i 11 (q 4 5 (a 2 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) (q {num})))' # noqa |
| 1962 | + |
| 1963 | +# this program: |
| 1964 | +# (mod (A) |
| 1965 | +# (defun append (L B) |
| 1966 | +# (if L |
| 1967 | +# (c (f L) (append (r L) B)) |
| 1968 | +# (c B ()) |
| 1969 | +# ) |
| 1970 | +# ) |
| 1971 | +# (defun iter (V N) |
| 1972 | +# (if N (c (append V N) (iter V (- N 1))) ()) |
| 1973 | +# ) |
| 1974 | +# (iter (q 51 "abababababababababababababababab") A) |
| 1975 | +# ) |
| 1976 | +# creates {num} CREATE_COIN conditions, each with a different amount |
| 1977 | +CREATE_UNIQUE_COINS = '(a (q 2 6 (c 2 (c (q 51 "abababababababababababababababab") (c 5 ())))) (c (q (a (i 5 (q 4 9 (a 4 (c 2 (c 13 (c 11 ()))))) (q 4 11 ())) 1) 2 (i 11 (q 4 (a 4 (c 2 (c 5 (c 11 ())))) (a 6 (c 2 (c 5 (c (- 11 (q . 1)) ()))))) ()) 1) (q {num})))' # noqa |
| 1978 | + |
| 1979 | + |
| 1980 | +class TestMaliciousGenerators: |
| 1981 | + |
| 1982 | + # TODO: create a lot of announcements. The messages can be made different by |
| 1983 | + # using substr on a large buffer |
| 1984 | + |
| 1985 | + # for all the height/time locks, we should only return the most strict |
| 1986 | + # condition, not all of them |
| 1987 | + @pytest.mark.parametrize( |
| 1988 | + "opcode", |
| 1989 | + [ |
| 1990 | + ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, |
| 1991 | + ConditionOpcode.ASSERT_HEIGHT_RELATIVE, |
| 1992 | + ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, |
| 1993 | + ConditionOpcode.ASSERT_SECONDS_RELATIVE, |
| 1994 | + ], |
| 1995 | + ) |
| 1996 | + def test_duplicate_large_integer_ladder(self, opcode): |
| 1997 | + condition = SINGLE_ARG_INT_LADDER_COND.format(opcode=opcode.value[0], num=28, filler="0x00") |
| 1998 | + start_time = time() |
| 1999 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2000 | + run_time = time() - start_time |
| 2001 | + assert npc_result.error is None |
| 2002 | + assert len(npc_result.npc_list) == 1 |
| 2003 | + assert npc_result.npc_list[0].conditions == [ |
| 2004 | + ( |
| 2005 | + opcode, |
| 2006 | + [ConditionWithArgs(opcode, [int_to_bytes(28)])], |
| 2007 | + ) |
| 2008 | + ] |
| 2009 | + assert run_time < 1 |
| 2010 | + print(f"run time:{run_time}") |
| 2011 | + |
| 2012 | + @pytest.mark.parametrize( |
| 2013 | + "opcode", |
| 2014 | + [ |
| 2015 | + ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, |
| 2016 | + ConditionOpcode.ASSERT_HEIGHT_RELATIVE, |
| 2017 | + ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, |
| 2018 | + ConditionOpcode.ASSERT_SECONDS_RELATIVE, |
| 2019 | + ], |
| 2020 | + ) |
| 2021 | + def test_duplicate_large_integer(self, opcode): |
| 2022 | + condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00") |
| 2023 | + start_time = time() |
| 2024 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2025 | + run_time = time() - start_time |
| 2026 | + assert npc_result.error is None |
| 2027 | + assert len(npc_result.npc_list) == 1 |
| 2028 | + assert npc_result.npc_list[0].conditions == [ |
| 2029 | + ( |
| 2030 | + opcode, |
| 2031 | + [ConditionWithArgs(opcode, [bytes([100])])], |
| 2032 | + ) |
| 2033 | + ] |
| 2034 | + assert run_time < 2 |
| 2035 | + print(f"run time:{run_time}") |
| 2036 | + |
| 2037 | + @pytest.mark.parametrize( |
| 2038 | + "opcode", |
| 2039 | + [ |
| 2040 | + ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, |
| 2041 | + ConditionOpcode.ASSERT_HEIGHT_RELATIVE, |
| 2042 | + ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, |
| 2043 | + ConditionOpcode.ASSERT_SECONDS_RELATIVE, |
| 2044 | + ], |
| 2045 | + ) |
| 2046 | + def test_duplicate_large_integer_substr(self, opcode): |
| 2047 | + condition = SINGLE_ARG_INT_SUBSTR_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00") |
| 2048 | + start_time = time() |
| 2049 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2050 | + run_time = time() - start_time |
| 2051 | + assert npc_result.error is None |
| 2052 | + assert len(npc_result.npc_list) == 1 |
| 2053 | + assert npc_result.npc_list[0].conditions == [ |
| 2054 | + ( |
| 2055 | + opcode, |
| 2056 | + [ConditionWithArgs(opcode, [bytes([100])])], |
| 2057 | + ) |
| 2058 | + ] |
| 2059 | + assert run_time < 3 |
| 2060 | + print(f"run time:{run_time}") |
| 2061 | + |
| 2062 | + @pytest.mark.parametrize( |
| 2063 | + "opcode", |
| 2064 | + [ |
| 2065 | + ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, |
| 2066 | + ConditionOpcode.ASSERT_HEIGHT_RELATIVE, |
| 2067 | + ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, |
| 2068 | + ConditionOpcode.ASSERT_SECONDS_RELATIVE, |
| 2069 | + ], |
| 2070 | + ) |
| 2071 | + def test_duplicate_large_integer_substr_tail(self, opcode): |
| 2072 | + condition = SINGLE_ARG_INT_SUBSTR_TAIL_COND.format( |
| 2073 | + opcode=opcode.value[0], num=280, val="0xffffffff", filler="0x00" |
| 2074 | + ) |
| 2075 | + start_time = time() |
| 2076 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2077 | + run_time = time() - start_time |
| 2078 | + assert npc_result.error is None |
| 2079 | + assert len(npc_result.npc_list) == 1 |
| 2080 | + |
| 2081 | + print(npc_result.npc_list[0].conditions[0][1]) |
| 2082 | + assert ConditionWithArgs(opcode, [int_to_bytes(0xFFFFFFFF)]) in npc_result.npc_list[0].conditions[0][1] |
| 2083 | + assert run_time < 1 |
| 2084 | + print(f"run time:{run_time}") |
| 2085 | + |
| 2086 | + @pytest.mark.parametrize( |
| 2087 | + "opcode", |
| 2088 | + [ |
| 2089 | + ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, |
| 2090 | + ConditionOpcode.ASSERT_HEIGHT_RELATIVE, |
| 2091 | + ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, |
| 2092 | + ConditionOpcode.ASSERT_SECONDS_RELATIVE, |
| 2093 | + ], |
| 2094 | + ) |
| 2095 | + def test_duplicate_large_integer_negative(self, opcode): |
| 2096 | + condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0xff") |
| 2097 | + start_time = time() |
| 2098 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2099 | + run_time = time() - start_time |
| 2100 | + assert npc_result.error is None |
| 2101 | + assert len(npc_result.npc_list) == 1 |
| 2102 | + assert npc_result.npc_list[0].conditions == [] |
| 2103 | + assert run_time < 2 |
| 2104 | + print(f"run time:{run_time}") |
| 2105 | + |
| 2106 | + def test_duplicate_reserve_fee(self): |
| 2107 | + opcode = ConditionOpcode.RESERVE_FEE |
| 2108 | + condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=280000, val=100, filler="0x00") |
| 2109 | + start_time = time() |
| 2110 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2111 | + run_time = time() - start_time |
| 2112 | + assert npc_result.error is None |
| 2113 | + assert len(npc_result.npc_list) == 1 |
| 2114 | + assert npc_result.npc_list[0].conditions == [ |
| 2115 | + ( |
| 2116 | + opcode.value, |
| 2117 | + [ConditionWithArgs(opcode, [int_to_bytes(100 * 280000)])], |
| 2118 | + ) |
| 2119 | + ] |
| 2120 | + assert run_time < 2 |
| 2121 | + print(f"run time:{run_time}") |
| 2122 | + |
| 2123 | + def test_duplicate_reserve_fee_negative(self): |
| 2124 | + opcode = ConditionOpcode.RESERVE_FEE |
| 2125 | + condition = SINGLE_ARG_INT_COND.format(opcode=opcode.value[0], num=200000, val=100, filler="0xff") |
| 2126 | + start_time = time() |
| 2127 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2128 | + run_time = time() - start_time |
| 2129 | + # RESERVE_FEE conditions fail unconditionally if they have a negative |
| 2130 | + # amount |
| 2131 | + assert npc_result.error == Err.RESERVE_FEE_CONDITION_FAILED.value |
| 2132 | + assert len(npc_result.npc_list) == 0 |
| 2133 | + assert run_time < 1 |
| 2134 | + print(f"run time:{run_time}") |
| 2135 | + |
| 2136 | + @pytest.mark.parametrize( |
| 2137 | + "opcode", [ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT] |
| 2138 | + ) |
| 2139 | + def test_duplicate_coin_announces(self, opcode): |
| 2140 | + condition = CREATE_ANNOUNCE_COND.format(opcode=opcode.value[0], num=5950000) |
| 2141 | + start_time = time() |
| 2142 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2143 | + run_time = time() - start_time |
| 2144 | + assert npc_result.error is None |
| 2145 | + assert len(npc_result.npc_list) == 1 |
| 2146 | + # coin announcements are not propagated to python, but validated in rust |
| 2147 | + assert len(npc_result.npc_list[0].conditions) == 0 |
| 2148 | + # TODO: optimize clvm to make this run in < 1 second |
| 2149 | + assert run_time < 13 |
| 2150 | + print(f"run time:{run_time}") |
| 2151 | + |
| 2152 | + def test_create_coin_duplicates(self): |
| 2153 | + # CREATE_COIN |
| 2154 | + # this program will emit 6000 identical CREATE_COIN conditions. However, |
| 2155 | + # we'll just end up looking at two of them, and fail at the first |
| 2156 | + # duplicate |
| 2157 | + condition = CREATE_COIN.format(num=600000) |
| 2158 | + start_time = time() |
| 2159 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2160 | + run_time = time() - start_time |
| 2161 | + assert npc_result.error == Err.DUPLICATE_OUTPUT.value |
| 2162 | + assert len(npc_result.npc_list) == 0 |
| 2163 | + assert run_time < 2 |
| 2164 | + print(f"run time:{run_time}") |
| 2165 | + |
| 2166 | + def test_many_create_coin(self): |
| 2167 | + # CREATE_COIN |
| 2168 | + # this program will emit many CREATE_COIN conditions, all with different |
| 2169 | + # amounts. |
| 2170 | + # the number 6095 was chosen carefully to not exceed the maximum cost |
| 2171 | + condition = CREATE_UNIQUE_COINS.format(num=6094) |
| 2172 | + start_time = time() |
| 2173 | + npc_result = generator_condition_tester(condition, quote=False) |
| 2174 | + run_time = time() - start_time |
| 2175 | + assert npc_result.error is None |
| 2176 | + assert len(npc_result.npc_list) == 1 |
| 2177 | + assert len(npc_result.npc_list[0].conditions) == 1 |
| 2178 | + assert npc_result.npc_list[0].conditions[0][0] == ConditionOpcode.CREATE_COIN.value |
| 2179 | + assert len(npc_result.npc_list[0].conditions[0][1]) == 6094 |
| 2180 | + assert run_time < 1 |
| 2181 | + print(f"run time:{run_time}") |
0 commit comments